Home | History | Annotate | Line # | Download | only in gcc
      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