]>
Commit | Line | Data |
---|---|---|
6cf276dd | 1 | /* SARIF output for diagnostics |
aeee4812 | 2 | Copyright (C) 2018-2023 Free Software Foundation, Inc. |
6cf276dd DM |
3 | Contributed by David Malcolm <dmalcolm@redhat.com>. |
4 | ||
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, | |
51 | diagnostic_info *diagnostic, | |
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, | |
70 | diagnostic_info *diagnostic, | |
71 | diagnostic_t orig_diag_kind, | |
72 | sarif_builder *builder); | |
4f01ae37 DM |
73 | void on_diagram (diagnostic_context *context, |
74 | const diagnostic_diagram &diagram, | |
75 | sarif_builder *builder); | |
6cf276dd DM |
76 | |
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, | |
93 | diagnostic_info *diagnostic, | |
94 | sarif_builder *builder); | |
95 | }; | |
96 | ||
3a1e9f3e DM |
97 | /* Subclass of sarif_object for SARIF threadFlow objects |
98 | (SARIF v2.1.0 section 3.37) for PATH. */ | |
99 | ||
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: | |
160 | sarif_builder (diagnostic_context *context); | |
161 | ||
162 | void end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic, | |
163 | diagnostic_t orig_diag_kind); | |
4f01ae37 DM |
164 | void emit_diagram (diagnostic_context *context, |
165 | const diagnostic_diagram &diagram); | |
6cf276dd DM |
166 | void end_group (); |
167 | ||
168 | void flush_to_file (FILE *outf); | |
169 | ||
79aaba0a | 170 | json::array *make_locations_arr (diagnostic_info *diagnostic); |
6cf276dd DM |
171 | json::object *make_location_object (const rich_location &rich_loc, |
172 | const logical_location *logical_loc); | |
173 | json::object *make_message_object (const char *msg) const; | |
4f01ae37 DM |
174 | json::object * |
175 | make_message_object_for_diagram (diagnostic_context *context, | |
176 | const diagnostic_diagram &diagram); | |
6cf276dd DM |
177 | |
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); | |
6cf276dd | 188 | json::object * |
3a1e9f3e DM |
189 | make_thread_flow_location_object (const diagnostic_event &event, |
190 | int path_event_idx); | |
6cf276dd DM |
191 | json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const; |
192 | json::object *maybe_make_physical_location_object (location_t loc); | |
193 | json::object *make_artifact_location_object (location_t loc); | |
194 | json::object *make_artifact_location_object (const char *filename); | |
195 | json::object *make_artifact_location_object_for_pwd () const; | |
196 | json::object *maybe_make_region_object (location_t loc) const; | |
197 | json::object *maybe_make_region_object_for_context (location_t loc) const; | |
198 | json::object *make_region_object_for_hint (const fixit_hint &hint) const; | |
199 | json::object *make_multiformat_message_string (const char *msg) const; | |
79aaba0a DM |
200 | json::object *make_top_level_object (sarif_invocation *invocation_obj, |
201 | json::array *results); | |
202 | json::object *make_run_object (sarif_invocation *invocation_obj, | |
203 | json::array *results); | |
6cf276dd DM |
204 | json::object *make_tool_object () const; |
205 | json::object *make_driver_tool_component_object () const; | |
206 | json::array *maybe_make_taxonomies_array () const; | |
207 | json::object *maybe_make_cwe_taxonomy_object () const; | |
208 | json::object *make_tool_component_reference_object_for_cwe () const; | |
209 | json::object * | |
210 | make_reporting_descriptor_object_for_warning (diagnostic_context *context, | |
211 | diagnostic_info *diagnostic, | |
212 | diagnostic_t orig_diag_kind, | |
213 | const char *option_text); | |
214 | json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const; | |
215 | json::object * | |
216 | make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id); | |
217 | json::object *make_artifact_object (const char *filename); | |
218 | json::object *maybe_make_artifact_content_object (const char *filename) const; | |
219 | json::object *maybe_make_artifact_content_object (const char *filename, | |
220 | int start_line, | |
221 | int end_line) const; | |
222 | json::object *make_fix_object (const rich_location &rich_loc); | |
223 | json::object *make_artifact_change_object (const rich_location &richloc); | |
224 | json::object *make_replacement_object (const fixit_hint &hint) const; | |
225 | json::object *make_artifact_content_object (const char *text) const; | |
226 | int get_sarif_column (expanded_location exploc) const; | |
227 | ||
228 | diagnostic_context *m_context; | |
229 | ||
79aaba0a DM |
230 | /* The JSON object for the invocation object. */ |
231 | sarif_invocation *m_invocation_obj; | |
232 | ||
6cf276dd DM |
233 | /* The JSON array of pending diagnostics. */ |
234 | json::array *m_results_array; | |
235 | ||
236 | /* The JSON object for the result object (if any) in the current | |
237 | diagnostic group. */ | |
238 | sarif_result *m_cur_group_result; | |
239 | ||
240 | hash_set <const char *> m_filenames; | |
241 | bool m_seen_any_relative_paths; | |
242 | hash_set <free_string_hash> m_rule_id_set; | |
243 | json::array *m_rules_arr; | |
244 | ||
245 | /* The set of all CWE IDs we've seen, if any. */ | |
246 | hash_set <int_hash <int, 0, 1> > m_cwe_id_set; | |
247 | ||
248 | int m_tabstop; | |
249 | }; | |
250 | ||
251 | static sarif_builder *the_builder; | |
252 | ||
75d62394 DM |
253 | /* class sarif_object : public json::object. */ |
254 | ||
255 | sarif_property_bag & | |
256 | sarif_object::get_or_create_properties () | |
257 | { | |
258 | json::value *properties_val = get ("properties"); | |
259 | if (properties_val) | |
260 | { | |
261 | if (properties_val->get_kind () == json::JSON_OBJECT) | |
262 | return *static_cast <sarif_property_bag *> (properties_val); | |
263 | } | |
264 | ||
265 | sarif_property_bag *bag = new sarif_property_bag (); | |
266 | set ("properties", bag); | |
267 | return *bag; | |
268 | } | |
269 | ||
270 | /* class sarif_invocation : public sarif_object. */ | |
79aaba0a DM |
271 | |
272 | /* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT. | |
273 | Add an object representing the ICE to the notifications array. */ | |
274 | ||
275 | void | |
276 | sarif_invocation::add_notification_for_ice (diagnostic_context *context, | |
277 | diagnostic_info *diagnostic, | |
278 | sarif_builder *builder) | |
279 | { | |
280 | m_success = false; | |
281 | ||
282 | sarif_ice_notification *notification_obj | |
283 | = new sarif_ice_notification (context, diagnostic, builder); | |
284 | m_notifications_arr->append (notification_obj); | |
285 | } | |
286 | ||
287 | void | |
75d62394 | 288 | sarif_invocation::prepare_to_flush (diagnostic_context *context) |
79aaba0a DM |
289 | { |
290 | /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */ | |
291 | set ("executionSuccessful", new json::literal (m_success)); | |
292 | ||
293 | /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */ | |
294 | set ("toolExecutionNotifications", m_notifications_arr); | |
75d62394 DM |
295 | |
296 | /* Call client hook, allowing it to create a custom property bag for | |
297 | this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */ | |
298 | if (context->m_client_data_hooks) | |
299 | context->m_client_data_hooks->add_sarif_invocation_properties (*this); | |
79aaba0a DM |
300 | } |
301 | ||
75d62394 | 302 | /* class sarif_result : public sarif_object. */ |
6cf276dd DM |
303 | |
304 | /* Handle secondary diagnostics that occur within a diagnostic group. | |
305 | The closest SARIF seems to have to nested diagnostics is the | |
306 | "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22), | |
307 | so we lazily set this property and populate the array if and when | |
308 | secondary diagnostics occur (such as notes to a warning). */ | |
309 | ||
310 | void | |
311 | sarif_result::on_nested_diagnostic (diagnostic_context *context, | |
312 | diagnostic_info *diagnostic, | |
313 | diagnostic_t /*orig_diag_kind*/, | |
314 | sarif_builder *builder) | |
315 | { | |
6cf276dd DM |
316 | /* We don't yet generate meaningful logical locations for notes; |
317 | sometimes these will related to current_function_decl, but | |
318 | often they won't. */ | |
319 | json::object *location_obj | |
320 | = builder->make_location_object (*diagnostic->richloc, NULL); | |
321 | json::object *message_obj | |
322 | = builder->make_message_object (pp_formatted_text (context->printer)); | |
323 | pp_clear_output_area (context->printer); | |
324 | location_obj->set ("message", message_obj); | |
325 | ||
4f01ae37 DM |
326 | add_related_location (location_obj); |
327 | } | |
328 | ||
329 | /* Handle diagrams that occur within a diagnostic group. | |
330 | The closest thing in SARIF seems to be to add a location to the | |
331 | "releatedLocations" property (SARIF v2.1.0 section 3.27.22), | |
332 | and to put the diagram into the "message" property of that location | |
333 | (SARIF v2.1.0 section 3.28.5). */ | |
334 | ||
335 | void | |
336 | sarif_result::on_diagram (diagnostic_context *context, | |
337 | const diagnostic_diagram &diagram, | |
338 | sarif_builder *builder) | |
339 | { | |
340 | json::object *location_obj = new json::object (); | |
341 | json::object *message_obj | |
342 | = builder->make_message_object_for_diagram (context, diagram); | |
343 | location_obj->set ("message", message_obj); | |
344 | ||
345 | add_related_location (location_obj); | |
346 | } | |
347 | ||
348 | /* Add LOCATION_OBJ to this result's "relatedLocations" array, | |
349 | creating it if it doesn't yet exist. */ | |
350 | ||
351 | void | |
352 | sarif_result::add_related_location (json::object *location_obj) | |
353 | { | |
354 | if (!m_related_locations_arr) | |
355 | { | |
356 | m_related_locations_arr = new json::array (); | |
357 | set ("relatedLocations", m_related_locations_arr); | |
358 | } | |
6cf276dd DM |
359 | m_related_locations_arr->append (location_obj); |
360 | } | |
361 | ||
75d62394 | 362 | /* class sarif_ice_notification : public sarif_object. */ |
79aaba0a DM |
363 | |
364 | /* sarif_ice_notification's ctor. | |
365 | DIAGNOSTIC is an internal compiler error. */ | |
366 | ||
367 | sarif_ice_notification::sarif_ice_notification (diagnostic_context *context, | |
368 | diagnostic_info *diagnostic, | |
369 | sarif_builder *builder) | |
370 | { | |
371 | /* "locations" property (SARIF v2.1.0 section 3.58.4). */ | |
372 | json::array *locations_arr = builder->make_locations_arr (diagnostic); | |
373 | set ("locations", locations_arr); | |
374 | ||
375 | /* "message" property (SARIF v2.1.0 section 3.85.5). */ | |
376 | json::object *message_obj | |
377 | = builder->make_message_object (pp_formatted_text (context->printer)); | |
378 | pp_clear_output_area (context->printer); | |
379 | set ("message", message_obj); | |
380 | ||
381 | /* "level" property (SARIF v2.1.0 section 3.58.6). */ | |
382 | set ("level", new json::string ("error")); | |
383 | } | |
384 | ||
3a1e9f3e DM |
385 | /* class sarif_thread_flow : public sarif_object. */ |
386 | ||
387 | sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread) | |
388 | { | |
389 | /* "id" property (SARIF v2.1.0 section 3.37.2). */ | |
390 | label_text name (thread.get_name (false)); | |
391 | set ("id", new json::string (name.get ())); | |
392 | ||
393 | /* "locations" property (SARIF v2.1.0 section 3.37.6). */ | |
394 | m_locations_arr = new json::array (); | |
395 | set ("locations", m_locations_arr); | |
396 | } | |
397 | ||
6cf276dd DM |
398 | /* class sarif_builder. */ |
399 | ||
400 | /* sarif_builder's ctor. */ | |
401 | ||
402 | sarif_builder::sarif_builder (diagnostic_context *context) | |
403 | : m_context (context), | |
79aaba0a | 404 | m_invocation_obj (new sarif_invocation ()), |
6cf276dd DM |
405 | m_results_array (new json::array ()), |
406 | m_cur_group_result (NULL), | |
407 | m_seen_any_relative_paths (false), | |
408 | m_rule_id_set (), | |
409 | m_rules_arr (new json::array ()), | |
410 | m_tabstop (context->tabstop) | |
411 | { | |
412 | } | |
413 | ||
414 | /* Implementation of "end_diagnostic" for SARIF output. */ | |
415 | ||
416 | void | |
417 | sarif_builder::end_diagnostic (diagnostic_context *context, | |
418 | diagnostic_info *diagnostic, | |
419 | diagnostic_t orig_diag_kind) | |
420 | { | |
79aaba0a DM |
421 | if (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT) |
422 | { | |
423 | m_invocation_obj->add_notification_for_ice (context, diagnostic, this); | |
424 | return; | |
425 | } | |
6cf276dd DM |
426 | |
427 | if (m_cur_group_result) | |
428 | /* Nested diagnostic. */ | |
429 | m_cur_group_result->on_nested_diagnostic (context, | |
430 | diagnostic, | |
431 | orig_diag_kind, | |
432 | this); | |
433 | else | |
434 | { | |
435 | /* Top-level diagnostic. */ | |
436 | sarif_result *result_obj | |
437 | = make_result_object (context, diagnostic, orig_diag_kind); | |
438 | m_results_array->append (result_obj); | |
439 | m_cur_group_result = result_obj; | |
440 | } | |
441 | } | |
442 | ||
4f01ae37 DM |
443 | /* Implementation of diagnostic_context::m_diagrams.m_emission_cb |
444 | for SARIF output. */ | |
445 | ||
446 | void | |
447 | sarif_builder::emit_diagram (diagnostic_context *context, | |
448 | const diagnostic_diagram &diagram) | |
449 | { | |
450 | /* We must be within the emission of a top-level diagnostic. */ | |
451 | gcc_assert (m_cur_group_result); | |
452 | m_cur_group_result->on_diagram (context, diagram, this); | |
453 | } | |
454 | ||
6cf276dd DM |
455 | /* Implementation of "end_group_cb" for SARIF output. */ |
456 | ||
457 | void | |
458 | sarif_builder::end_group () | |
459 | { | |
460 | m_cur_group_result = NULL; | |
461 | } | |
462 | ||
463 | /* Create a top-level object, and add it to all the results | |
464 | (and other entities) we've seen so far. | |
465 | ||
466 | Flush it all to OUTF. */ | |
467 | ||
468 | void | |
469 | sarif_builder::flush_to_file (FILE *outf) | |
470 | { | |
75d62394 | 471 | m_invocation_obj->prepare_to_flush (m_context); |
79aaba0a | 472 | json::object *top = make_top_level_object (m_invocation_obj, m_results_array); |
6cf276dd | 473 | top->dump (outf); |
79aaba0a | 474 | m_invocation_obj = NULL; |
6cf276dd DM |
475 | m_results_array = NULL; |
476 | fprintf (outf, "\n"); | |
477 | delete top; | |
478 | } | |
479 | ||
480 | /* Attempt to convert DIAG_KIND to a suitable value for the "level" | |
481 | property (SARIF v2.1.0 section 3.27.10). | |
482 | ||
483 | Return NULL if there isn't one. */ | |
484 | ||
485 | static const char * | |
486 | maybe_get_sarif_level (diagnostic_t diag_kind) | |
487 | { | |
488 | switch (diag_kind) | |
489 | { | |
490 | case DK_WARNING: | |
491 | return "warning"; | |
492 | case DK_ERROR: | |
493 | return "error"; | |
494 | case DK_NOTE: | |
495 | case DK_ANACHRONISM: | |
496 | return "note"; | |
497 | default: | |
498 | return NULL; | |
499 | } | |
500 | } | |
501 | ||
502 | /* Make a string for DIAG_KIND suitable for use a ruleId | |
503 | (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't | |
504 | have anything better to use. */ | |
505 | ||
506 | static char * | |
507 | make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind) | |
508 | { | |
509 | static const char *const diagnostic_kind_text[] = { | |
510 | #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T), | |
511 | #include "diagnostic.def" | |
512 | #undef DEFINE_DIAGNOSTIC_KIND | |
513 | "must-not-happen" | |
514 | }; | |
515 | /* Lose the trailing ": ". */ | |
516 | const char *kind_text = diagnostic_kind_text[diag_kind]; | |
517 | size_t len = strlen (kind_text); | |
518 | gcc_assert (len > 2); | |
519 | gcc_assert (kind_text[len - 2] == ':'); | |
520 | gcc_assert (kind_text[len - 1] == ' '); | |
521 | char *rstrip = xstrdup (kind_text); | |
522 | rstrip[len - 2] = '\0'; | |
523 | return rstrip; | |
524 | } | |
525 | ||
526 | /* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */ | |
527 | ||
528 | sarif_result * | |
529 | sarif_builder::make_result_object (diagnostic_context *context, | |
530 | diagnostic_info *diagnostic, | |
531 | diagnostic_t orig_diag_kind) | |
532 | { | |
533 | sarif_result *result_obj = new sarif_result (); | |
534 | ||
535 | /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */ | |
536 | /* Ideally we'd have an option_name for these. */ | |
537 | if (char *option_text | |
538 | = context->option_name (context, diagnostic->option_index, | |
539 | orig_diag_kind, diagnostic->kind)) | |
540 | { | |
541 | /* Lazily create reportingDescriptor objects for and add to m_rules_arr. | |
542 | Set ruleId referencing them. */ | |
543 | result_obj->set ("ruleId", new json::string (option_text)); | |
544 | if (m_rule_id_set.contains (option_text)) | |
545 | free (option_text); | |
546 | else | |
547 | { | |
548 | /* This is the first time we've seen this ruleId. */ | |
549 | /* Add to set, taking ownership. */ | |
550 | m_rule_id_set.add (option_text); | |
551 | ||
552 | json::object *reporting_desc_obj | |
553 | = make_reporting_descriptor_object_for_warning (context, | |
554 | diagnostic, | |
555 | orig_diag_kind, | |
556 | option_text); | |
557 | m_rules_arr->append (reporting_desc_obj); | |
558 | } | |
559 | } | |
560 | else | |
561 | { | |
562 | /* Otherwise, we have an "error" or a stray "note"; use the | |
563 | diagnostic kind as the ruleId, so that the result object at least | |
564 | has a ruleId. | |
565 | We don't bother creating reportingDescriptor objects for these. */ | |
566 | char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind); | |
567 | result_obj->set ("ruleId", new json::string (rule_id)); | |
568 | free (rule_id); | |
569 | } | |
570 | ||
571 | /* "taxa" property (SARIF v2.1.0 section 3.27.8). */ | |
572 | if (diagnostic->metadata) | |
573 | if (int cwe_id = diagnostic->metadata->get_cwe ()) | |
574 | { | |
575 | json::array *taxa_arr = new json::array (); | |
576 | json::object *cwe_id_obj | |
577 | = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id); | |
578 | taxa_arr->append (cwe_id_obj); | |
579 | result_obj->set ("taxa", taxa_arr); | |
580 | } | |
581 | ||
582 | /* "level" property (SARIF v2.1.0 section 3.27.10). */ | |
583 | if (const char *sarif_level = maybe_get_sarif_level (diagnostic->kind)) | |
584 | result_obj->set ("level", new json::string (sarif_level)); | |
585 | ||
586 | /* "message" property (SARIF v2.1.0 section 3.27.11). */ | |
587 | json::object *message_obj | |
588 | = make_message_object (pp_formatted_text (context->printer)); | |
589 | pp_clear_output_area (context->printer); | |
590 | result_obj->set ("message", message_obj); | |
591 | ||
592 | /* "locations" property (SARIF v2.1.0 section 3.27.12). */ | |
79aaba0a | 593 | json::array *locations_arr = make_locations_arr (diagnostic); |
6cf276dd DM |
594 | result_obj->set ("locations", locations_arr); |
595 | ||
596 | /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */ | |
597 | if (const diagnostic_path *path = diagnostic->richloc->get_path ()) | |
598 | { | |
599 | json::array *code_flows_arr = new json::array (); | |
600 | json::object *code_flow_obj = make_code_flow_object (*path); | |
601 | code_flows_arr->append (code_flow_obj); | |
602 | result_obj->set ("codeFlows", code_flows_arr); | |
603 | } | |
604 | ||
605 | /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is | |
606 | set up later, if any nested diagnostics occur within this diagnostic | |
607 | group. */ | |
608 | ||
609 | /* "fixes" property (SARIF v2.1.0 section 3.27.30). */ | |
610 | const rich_location *richloc = diagnostic->richloc; | |
611 | if (richloc->get_num_fixit_hints ()) | |
612 | { | |
613 | json::array *fix_arr = new json::array (); | |
614 | json::object *fix_obj = make_fix_object (*richloc); | |
615 | fix_arr->append (fix_obj); | |
616 | result_obj->set ("fixes", fix_arr); | |
617 | } | |
618 | ||
619 | return result_obj; | |
620 | } | |
621 | ||
622 | /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49) | |
623 | for a GCC warning. */ | |
624 | ||
625 | json::object * | |
626 | sarif_builder:: | |
627 | make_reporting_descriptor_object_for_warning (diagnostic_context *context, | |
628 | diagnostic_info *diagnostic, | |
629 | diagnostic_t /*orig_diag_kind*/, | |
630 | const char *option_text) | |
631 | { | |
632 | json::object *reporting_desc = new json::object (); | |
633 | ||
634 | /* "id" property (SARIF v2.1.0 section 3.49.3). */ | |
635 | reporting_desc->set ("id", new json::string (option_text)); | |
636 | ||
637 | /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since | |
638 | it seems redundant compared to "id". */ | |
639 | ||
640 | /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */ | |
641 | if (context->get_option_url) | |
642 | { | |
643 | char *option_url | |
644 | = context->get_option_url (context, diagnostic->option_index); | |
645 | if (option_url) | |
646 | { | |
647 | reporting_desc->set ("helpUri", new json::string (option_url)); | |
648 | free (option_url); | |
649 | } | |
650 | } | |
651 | ||
652 | return reporting_desc; | |
653 | } | |
654 | ||
655 | /* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49) | |
656 | for CWE_ID, for use within the CWE taxa array. */ | |
657 | ||
658 | json::object * | |
659 | sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const | |
660 | { | |
661 | json::object *reporting_desc = new json::object (); | |
662 | ||
663 | /* "id" property (SARIF v2.1.0 section 3.49.3). */ | |
664 | { | |
665 | pretty_printer pp; | |
666 | pp_printf (&pp, "%i", cwe_id); | |
667 | reporting_desc->set ("id", new json::string (pp_formatted_text (&pp))); | |
668 | } | |
669 | ||
670 | /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */ | |
671 | { | |
672 | char *url = get_cwe_url (cwe_id); | |
673 | reporting_desc->set ("helpUri", new json::string (url)); | |
674 | free (url); | |
675 | } | |
676 | ||
677 | return reporting_desc; | |
678 | } | |
679 | ||
680 | /* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52) | |
681 | referencing CWE_ID, for use within a result object. | |
682 | Also, add CWE_ID to m_cwe_id_set. */ | |
683 | ||
684 | json::object * | |
685 | sarif_builder:: | |
686 | make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id) | |
687 | { | |
688 | json::object *desc_ref_obj = new json::object (); | |
689 | ||
690 | /* "id" property (SARIF v2.1.0 section 3.52.4). */ | |
691 | { | |
692 | pretty_printer pp; | |
693 | pp_printf (&pp, "%i", cwe_id); | |
694 | desc_ref_obj->set ("id", new json::string (pp_formatted_text (&pp))); | |
695 | } | |
696 | ||
697 | /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */ | |
698 | json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe (); | |
699 | desc_ref_obj->set ("toolComponent", comp_ref_obj); | |
700 | ||
701 | /* Add CWE_ID to our set. */ | |
702 | gcc_assert (cwe_id > 0); | |
703 | m_cwe_id_set.add (cwe_id); | |
704 | ||
705 | return desc_ref_obj; | |
706 | } | |
707 | ||
708 | /* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that | |
709 | references the CWE taxonomy. */ | |
710 | ||
711 | json::object * | |
712 | sarif_builder:: | |
713 | make_tool_component_reference_object_for_cwe () const | |
714 | { | |
715 | json::object *comp_ref_obj = new json::object (); | |
716 | ||
717 | /* "name" property (SARIF v2.1.0 section 3.54.3). */ | |
718 | comp_ref_obj->set ("name", new json::string ("cwe")); | |
719 | ||
720 | return comp_ref_obj; | |
721 | } | |
722 | ||
79aaba0a DM |
723 | /* Make an array suitable for use as the "locations" property of: |
724 | - a "result" object (SARIF v2.1.0 section 3.27.12), or | |
725 | - a "notification" object (SARIF v2.1.0 section 3.58.4). */ | |
726 | ||
727 | json::array * | |
728 | sarif_builder::make_locations_arr (diagnostic_info *diagnostic) | |
729 | { | |
730 | json::array *locations_arr = new json::array (); | |
731 | const logical_location *logical_loc = NULL; | |
732 | if (m_context->m_client_data_hooks) | |
733 | logical_loc | |
734 | = m_context->m_client_data_hooks->get_current_logical_location (); | |
735 | ||
736 | json::object *location_obj | |
737 | = make_location_object (*diagnostic->richloc, logical_loc); | |
738 | locations_arr->append (location_obj); | |
739 | return locations_arr; | |
740 | } | |
741 | ||
6cf276dd DM |
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); | |
753 | json::array *location_locs_arr = new json::array (); | |
754 | location_locs_arr->append (logical_loc_obj); | |
755 | location_obj->set ("logicalLocations", 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 ("physicalLocation", 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 ("physicalLocation", 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 (false); | |
f858fe7a | 799 | json::object *message_obj = make_message_object (ev_desc.get ()); |
6cf276dd | 800 | location_obj->set ("message", message_obj); |
6cf276dd DM |
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 | { | |
2e8a0553 | 812 | if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == NULL) |
6cf276dd DM |
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 ("artifactLocation", 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 ("region", 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 ("contextRegion", 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 ("uri", 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 ("uriBaseId", 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 (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 ("uri", new json::string (pwd)); | |
912 | free (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 ("startLine", new json::integer_number (exploc_start.line)); | |
954 | ||
955 | /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */ | |
956 | region_obj->set ("startColumn", | |
957 | new json::integer_number (get_sarif_column (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 ("endLine", 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_finish) + 1; | |
967 | region_obj->set ("endColumn", 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 ("startLine", 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 ("endLine", 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 (exploc_start.file, | |
1013 | exploc_start.line, | |
1014 | exploc_finish.line)) | |
1015 | region_obj->set ("snippet", 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 ("startLine", 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_start); | |
1039 | region_obj->set ("startColumn", | |
1040 | 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 ("endLine", 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_next); | |
1049 | region_obj->set ("endColumn", 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 ("name", 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 ("fullyQualifiedName", | |
1103 | 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 ("decoratedName", 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 ("kind", 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 | ||
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 DM |
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 (ev); | |
1167 | thread_flow_loc_obj->set ("location", 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 ("kinds", kinds_arr); | |
1173 | ||
1174 | /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */ | |
1175 | thread_flow_loc_obj->set ("nestingLevel", | |
1176 | new json::integer_number (ev.get_stack_depth ())); | |
1177 | ||
3a1e9f3e DM |
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 ("executionOrder", | |
1181 | new json::integer_number (path_event_idx + 1)); | |
1182 | ||
6cf276dd DM |
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 (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 (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 (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 ("text", new json::string (msg)); | |
1226 | ||
1227 | return message_obj; | |
1228 | } | |
1229 | ||
4f01ae37 DM |
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 ("text", 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 (context->printer, " "); | |
1250 | pp_set_prefix (context->printer, saved_prefix); | |
1251 | ||
1252 | /* "markdown" property (SARIF v2.1.0 section 3.11.9). */ | |
1253 | message_obj->set ("markdown", | |
1254 | new json::string (pp_formatted_text (context->printer))); | |
1255 | ||
1256 | pp_clear_output_area (context->printer); | |
1257 | ||
1258 | return message_obj; | |
1259 | } | |
1260 | ||
6cf276dd DM |
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 ("text", 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). | |
79aaba0a | 1279 | Take ownership of INVOCATION_OBJ and RESULTS. */ |
6cf276dd DM |
1280 | |
1281 | json::object * | |
79aaba0a DM |
1282 | sarif_builder::make_top_level_object (sarif_invocation *invocation_obj, |
1283 | json::array *results) | |
6cf276dd DM |
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 ("$schema", new json::string (SARIF_SCHEMA)); | |
1289 | ||
1290 | /* "version" property (SARIF v2.1.0 section 3.13.2). */ | |
1291 | log_obj->set ("version", 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 (); | |
79aaba0a | 1295 | json::object *run_obj = make_run_object (invocation_obj, results); |
6cf276dd DM |
1296 | run_arr->append (run_obj); |
1297 | log_obj->set ("runs", run_arr); | |
1298 | ||
1299 | return log_obj; | |
1300 | } | |
1301 | ||
1302 | /* Make a run object (SARIF v2.1.0 section 3.14). | |
79aaba0a | 1303 | Take ownership of INVOCATION_OBJ and RESULTS. */ |
6cf276dd DM |
1304 | |
1305 | json::object * | |
79aaba0a DM |
1306 | sarif_builder::make_run_object (sarif_invocation *invocation_obj, |
1307 | json::array *results) | |
6cf276dd DM |
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 ("tool", 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 ("taxonomies", taxonomies_arr); | |
1318 | ||
79aaba0a DM |
1319 | /* "invocations" property (SARIF v2.1.0 section 3.14.11). */ |
1320 | { | |
1321 | json::array *invocations_arr = new json::array (); | |
1322 | invocations_arr->append (invocation_obj); | |
1323 | run_obj->set ("invocations", invocations_arr); | |
1324 | } | |
1325 | ||
6cf276dd DM |
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 ("originalUriBaseIds", 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, 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 (iter); | |
1340 | artifacts_arr->append (artifact_obj); | |
1341 | } | |
1342 | run_obj->set ("artifacts", artifacts_arr); | |
1343 | ||
1344 | /* "results" property (SARIF v2.1.0 section 3.14.23). */ | |
1345 | run_obj->set ("results", 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 ("driver", driver_obj); | |
1360 | ||
1361 | /* Report plugins via the "extensions" property | |
1362 | (SARIF v2.1.0 section 3.18.3). */ | |
1363 | if (m_context->m_client_data_hooks) | |
1364 | if (const client_version_info *vinfo | |
1365 | = m_context->m_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 (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 ("name", 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 ("fullName", 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 ("version", 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 ("extensions", extensions_arr); | |
1397 | for (auto iter : v.m_plugin_objs) | |
1398 | extensions_arr->append (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 (m_context->m_client_data_hooks) | |
1417 | if (const client_version_info *vinfo | |
1418 | = m_context->m_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 ("name", 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 ("fullName", new json::string (full_name)); | |
1428 | free (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 ("version", 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 ("informationUri", new json::string (version_url)); | |
1439 | free (version_url); | |
1440 | } | |
1441 | } | |
1442 | ||
1443 | /* "rules" property (SARIF v2.1.0 section 3.19.23). */ | |
1444 | driver_obj->set ("rules", 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 (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 ("name", new json::string ("CWE")); | |
1484 | ||
1485 | /* "version" property (SARIF v2.1.0 section 3.19.13). */ | |
1486 | taxonomy_obj->set ("version", new json::string ("4.7")); | |
1487 | ||
1488 | /* "organization" property (SARIF v2.1.0 section 3.19.18). */ | |
1489 | taxonomy_obj->set ("organization", 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 ("The MITRE" | |
1494 | " Common Weakness Enumeration"); | |
1495 | taxonomy_obj->set ("shortDescription", 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 (cwe_taxon); | |
1504 | } | |
1505 | taxonomy_obj->set ("taxa", 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 ("location", 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 ("contents", artifact_content_obj); | |
1525 | ||
1526 | /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */ | |
1527 | if (m_context->m_client_data_hooks) | |
1528 | if (const char *source_lang | |
1529 | = m_context->m_client_data_hooks->maybe_get_sarif_source_language | |
1530 | (filename)) | |
1531 | artifact_obj->set ("sourceLanguage", new json::string (source_lang)); | |
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 DM |
1542 | /* Let input.cc handle any charset conversion. */ |
1543 | char_span utf8_content = get_source_file_content (filename); | |
1544 | if (!utf8_content) | |
6cf276dd DM |
1545 | return NULL; |
1546 | ||
d495ea2b DM |
1547 | /* Don't add it if it's not valid UTF-8. */ |
1548 | if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ())) | |
1549 | return NULL; | |
6cf276dd | 1550 | |
d495ea2b DM |
1551 | json::object *artifact_content_obj = new json::object (); |
1552 | artifact_content_obj->set ("text", | |
1553 | new json::string (utf8_content.get_buffer (), | |
1554 | utf8_content.length ())); | |
6cf276dd DM |
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 | static char * | |
1562 | get_source_lines (const char *filename, | |
1563 | int start_line, | |
1564 | int end_line) | |
1565 | { | |
1566 | auto_vec<char> result; | |
1567 | ||
1568 | for (int line = start_line; line <= end_line; line++) | |
1569 | { | |
1570 | char_span line_content = location_get_source_line (filename, line); | |
1571 | if (!line_content.get_buffer ()) | |
1572 | return NULL; | |
1573 | result.reserve (line_content.length () + 1); | |
1574 | for (size_t i = 0; i < line_content.length (); i++) | |
1575 | result.quick_push (line_content[i]); | |
1576 | result.quick_push ('\n'); | |
1577 | } | |
1578 | result.safe_push ('\0'); | |
1579 | ||
1580 | return xstrdup (result.address ()); | |
1581 | } | |
1582 | ||
1583 | /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given | |
1584 | run of lines within FILENAME (including the endpoints). */ | |
1585 | ||
1586 | json::object * | |
1587 | sarif_builder::maybe_make_artifact_content_object (const char *filename, | |
1588 | int start_line, | |
1589 | int end_line) const | |
1590 | { | |
1591 | char *text_utf8 = get_source_lines (filename, start_line, end_line); | |
1592 | ||
1593 | if (!text_utf8) | |
1594 | return NULL; | |
1595 | ||
d495ea2b DM |
1596 | /* Don't add it if it's not valid UTF-8. */ |
1597 | if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8))) | |
1598 | { | |
1599 | free (text_utf8); | |
1600 | return NULL; | |
1601 | } | |
1602 | ||
6cf276dd DM |
1603 | json::object *artifact_content_obj = new json::object (); |
1604 | artifact_content_obj->set ("text", new json::string (text_utf8)); | |
1605 | free (text_utf8); | |
1606 | ||
1607 | return artifact_content_obj; | |
1608 | } | |
1609 | ||
1610 | /* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */ | |
1611 | ||
1612 | json::object * | |
1613 | sarif_builder::make_fix_object (const rich_location &richloc) | |
1614 | { | |
1615 | json::object *fix_obj = new json::object (); | |
1616 | ||
1617 | /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */ | |
1618 | /* We assume that all fix-it hints in RICHLOC affect the same file. */ | |
1619 | json::array *artifact_change_arr = new json::array (); | |
1620 | json::object *artifact_change_obj = make_artifact_change_object (richloc); | |
1621 | artifact_change_arr->append (artifact_change_obj); | |
1622 | fix_obj->set ("artifactChanges", artifact_change_arr); | |
1623 | ||
1624 | return fix_obj; | |
1625 | } | |
1626 | ||
1627 | /* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */ | |
1628 | ||
1629 | json::object * | |
1630 | sarif_builder::make_artifact_change_object (const rich_location &richloc) | |
1631 | { | |
1632 | json::object *artifact_change_obj = new json::object (); | |
1633 | ||
1634 | /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */ | |
1635 | json::object *artifact_location_obj | |
1636 | = make_artifact_location_object (richloc.get_loc ()); | |
1637 | artifact_change_obj->set ("artifactLocation", artifact_location_obj); | |
1638 | ||
1639 | /* "replacements" property (SARIF v2.1.0 section 3.56.3). */ | |
1640 | json::array *replacement_arr = new json::array (); | |
1641 | for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++) | |
1642 | { | |
1643 | const fixit_hint *hint = richloc.get_fixit_hint (i); | |
1644 | json::object *replacement_obj = make_replacement_object (*hint); | |
1645 | replacement_arr->append (replacement_obj); | |
1646 | } | |
1647 | artifact_change_obj->set ("replacements", replacement_arr); | |
1648 | ||
1649 | return artifact_change_obj; | |
1650 | } | |
1651 | ||
1652 | /* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */ | |
1653 | ||
1654 | json::object * | |
1655 | sarif_builder::make_replacement_object (const fixit_hint &hint) const | |
1656 | { | |
1657 | json::object *replacement_obj = new json::object (); | |
1658 | ||
1659 | /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */ | |
1660 | json::object *region_obj = make_region_object_for_hint (hint); | |
1661 | replacement_obj->set ("deletedRegion", region_obj); | |
1662 | ||
1663 | /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */ | |
1664 | json::object *content_obj = make_artifact_content_object (hint.get_string ()); | |
1665 | replacement_obj->set ("insertedContent", content_obj); | |
1666 | ||
1667 | return replacement_obj; | |
1668 | } | |
1669 | ||
1670 | /* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */ | |
1671 | ||
1672 | json::object * | |
1673 | sarif_builder::make_artifact_content_object (const char *text) const | |
1674 | { | |
1675 | json::object *content_obj = new json::object (); | |
1676 | ||
1677 | /* "text" property (SARIF v2.1.0 section 3.3.2). */ | |
1678 | content_obj->set ("text", new json::string (text)); | |
1679 | ||
1680 | return content_obj; | |
1681 | } | |
1682 | ||
1683 | /* No-op implementation of "begin_diagnostic" for SARIF output. */ | |
1684 | ||
1685 | static void | |
1686 | sarif_begin_diagnostic (diagnostic_context *, diagnostic_info *) | |
1687 | { | |
1688 | } | |
1689 | ||
1690 | /* Implementation of "end_diagnostic" for SARIF output. */ | |
1691 | ||
1692 | static void | |
1693 | sarif_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic, | |
1694 | diagnostic_t orig_diag_kind) | |
1695 | { | |
1696 | gcc_assert (the_builder); | |
1697 | the_builder->end_diagnostic (context, diagnostic, orig_diag_kind); | |
1698 | } | |
1699 | ||
1700 | /* No-op implementation of "begin_group_cb" for SARIF output. */ | |
1701 | ||
1702 | static void | |
1703 | sarif_begin_group (diagnostic_context *) | |
1704 | { | |
1705 | } | |
1706 | ||
1707 | /* Implementation of "end_group_cb" for SARIF output. */ | |
1708 | ||
1709 | static void | |
1710 | sarif_end_group (diagnostic_context *) | |
1711 | { | |
1712 | gcc_assert (the_builder); | |
1713 | the_builder->end_group (); | |
1714 | } | |
1715 | ||
1716 | /* Flush the top-level array to OUTF. */ | |
1717 | ||
1718 | static void | |
1719 | sarif_flush_to_file (FILE *outf) | |
1720 | { | |
1721 | gcc_assert (the_builder); | |
1722 | the_builder->flush_to_file (outf); | |
1723 | delete the_builder; | |
1724 | the_builder = NULL; | |
1725 | } | |
1726 | ||
1727 | /* Callback for final cleanup for SARIF output to stderr. */ | |
1728 | ||
1729 | static void | |
1730 | sarif_stderr_final_cb (diagnostic_context *) | |
1731 | { | |
1732 | gcc_assert (the_builder); | |
1733 | sarif_flush_to_file (stderr); | |
1734 | } | |
1735 | ||
1736 | static char *sarif_output_base_file_name; | |
1737 | ||
1738 | /* Callback for final cleanup for SARIF output to a file. */ | |
1739 | ||
1740 | static void | |
1741 | sarif_file_final_cb (diagnostic_context *) | |
1742 | { | |
1743 | char *filename = concat (sarif_output_base_file_name, ".sarif", NULL); | |
1744 | FILE *outf = fopen (filename, "w"); | |
1745 | if (!outf) | |
1746 | { | |
1747 | const char *errstr = xstrerror (errno); | |
1748 | fnotice (stderr, "error: unable to open '%s' for writing: %s\n", | |
1749 | filename, errstr); | |
1750 | free (filename); | |
1751 | return; | |
1752 | } | |
1753 | gcc_assert (the_builder); | |
1754 | sarif_flush_to_file (outf); | |
1755 | fclose (outf); | |
1756 | free (filename); | |
1757 | } | |
1758 | ||
79aaba0a DM |
1759 | /* Callback for diagnostic_context::ice_handler_cb for when an ICE |
1760 | occurs. */ | |
1761 | ||
1762 | static void | |
1763 | sarif_ice_handler (diagnostic_context *context) | |
1764 | { | |
1765 | /* Attempt to ensure that a .sarif file is written out. */ | |
1766 | diagnostic_finish (context); | |
1767 | ||
1768 | /* Print a header for the remaining output to stderr, and | |
1769 | return, attempting to print the usual ICE messages to | |
1770 | stderr. Hopefully this will be helpful to the user in | |
1771 | indicating what's gone wrong (also for DejaGnu, for pruning | |
1772 | those messages). */ | |
1773 | fnotice (stderr, "Internal compiler error:\n"); | |
1774 | } | |
1775 | ||
4f01ae37 DM |
1776 | /* Callback for diagnostic_context::m_diagrams.m_emission_cb. */ |
1777 | ||
1778 | static void | |
1779 | sarif_emit_diagram (diagnostic_context *context, | |
1780 | const diagnostic_diagram &diagram) | |
1781 | { | |
1782 | gcc_assert (the_builder); | |
1783 | the_builder->emit_diagram (context, diagram); | |
1784 | } | |
1785 | ||
6cf276dd DM |
1786 | /* Populate CONTEXT in preparation for SARIF output (either to stderr, or |
1787 | to a file). */ | |
1788 | ||
1789 | static void | |
1790 | diagnostic_output_format_init_sarif (diagnostic_context *context) | |
1791 | { | |
1792 | the_builder = new sarif_builder (context); | |
1793 | ||
1794 | /* Override callbacks. */ | |
1795 | context->begin_diagnostic = sarif_begin_diagnostic; | |
1796 | context->end_diagnostic = sarif_end_diagnostic; | |
1797 | context->begin_group_cb = sarif_begin_group; | |
1798 | context->end_group_cb = sarif_end_group; | |
1799 | context->print_path = NULL; /* handled in sarif_end_diagnostic. */ | |
79aaba0a | 1800 | context->ice_handler_cb = sarif_ice_handler; |
4f01ae37 | 1801 | context->m_diagrams.m_emission_cb = sarif_emit_diagram; |
6cf276dd DM |
1802 | |
1803 | /* The metadata is handled in SARIF format, rather than as text. */ | |
1804 | context->show_cwe = false; | |
0b14f590 | 1805 | context->show_rules = false; |
6cf276dd DM |
1806 | |
1807 | /* The option is handled in SARIF format, rather than as text. */ | |
1808 | context->show_option_requested = false; | |
1809 | ||
1810 | /* Don't colorize the text. */ | |
1811 | pp_show_color (context->printer) = false; | |
1812 | } | |
1813 | ||
1814 | /* Populate CONTEXT in preparation for SARIF output to stderr. */ | |
1815 | ||
1816 | void | |
1817 | diagnostic_output_format_init_sarif_stderr (diagnostic_context *context) | |
1818 | { | |
1819 | diagnostic_output_format_init_sarif (context); | |
1820 | context->final_cb = sarif_stderr_final_cb; | |
1821 | } | |
1822 | ||
1823 | /* Populate CONTEXT in preparation for SARIF output to a file named | |
1824 | BASE_FILE_NAME.sarif. */ | |
1825 | ||
1826 | void | |
1827 | diagnostic_output_format_init_sarif_file (diagnostic_context *context, | |
1828 | const char *base_file_name) | |
1829 | { | |
1830 | diagnostic_output_format_init_sarif (context); | |
1831 | context->final_cb = sarif_file_final_cb; | |
1832 | sarif_output_base_file_name = xstrdup (base_file_name); | |
1833 | } |