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