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