summaryrefslogtreecommitdiff
path: root/gcc/diagnostic-format-sarif.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/diagnostic-format-sarif.cc')
-rw-r--r--gcc/diagnostic-format-sarif.cc1586
1 files changed, 1586 insertions, 0 deletions
diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
new file mode 100644
index 00000000000..0c33179e8cf
--- /dev/null
+++ b/gcc/diagnostic-format-sarif.cc
@@ -0,0 +1,1586 @@
+/* SARIF output for diagnostics
+ Copyright (C) 2018-2022 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostic.h"
+#include "diagnostic-metadata.h"
+#include "diagnostic-path.h"
+#include "json.h"
+#include "cpplib.h"
+#include "logical-location.h"
+#include "diagnostic-client-data-hooks.h"
+
+class sarif_builder;
+
+/* Subclass of json::object for SARIF result objects
+ (SARIF v2.1.0 section 3.27. */
+
+class sarif_result : public json::object
+{
+public:
+ sarif_result () : m_related_locations_arr (NULL) {}
+
+ void
+ on_nested_diagnostic (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind,
+ sarif_builder *builder);
+
+private:
+ json::array *m_related_locations_arr;
+};
+
+/* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
+ and -fdiagnostics-format=sarif-file).
+
+ As diagnostics occur, we build "result" JSON objects, and
+ accumulate state:
+ - which source files are referenced
+ - which warnings are emitted
+ - which CWEs are used
+
+ At the end of the compile, we use the above to build the full SARIF
+ object tree, adding the result objects to the correct place, and
+ creating objects for the various source files, warnings and CWEs
+ referenced.
+
+ Implemented:
+ - fix-it hints
+ - CWE metadata
+ - diagnostic groups (see limitations below)
+ - logical locations (e.g. cfun)
+
+ Known limitations:
+ - GCC supports one-deep nesting of diagnostics (via auto_diagnostic_group),
+ but we only capture location and message information from such nested
+ diagnostics (e.g. we ignore fix-it hints on them)
+ - doesn't yet capture command-line arguments: would be run.invocations
+ property (SARIF v2.1.0 section 3.14.11), as invocation objects
+ (SARIF v2.1.0 section 3.20), but we'd want to capture the arguments to
+ toplev::main, and the response files.
+ - doesn't capture escape_on_output_p
+ - doesn't capture secondary locations within a rich_location
+ (perhaps we should use the "relatedLocations" property: SARIF v2.1.0
+ section 3.27.22)
+ - doesn't capture "artifact.encoding" property
+ (SARIF v2.1.0 section 3.24.9).
+ - doesn't capture hashes of the source files
+ ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11).
+ - doesn't capture the "analysisTarget" property
+ (SARIF v2.1.0 section 3.27.13).
+ - doesn't capture labelled ranges
+ - doesn't capture -Werror cleanly
+ - doesn't capture inlining information (can SARIF handle this?)
+ - doesn't capture macro expansion information (can SARIF handle this?). */
+
+class sarif_builder
+{
+public:
+ sarif_builder (diagnostic_context *context);
+
+ void end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind);
+
+ void end_group ();
+
+ void flush_to_file (FILE *outf);
+
+ json::object *make_location_object (const rich_location &rich_loc,
+ const logical_location *logical_loc);
+ json::object *make_message_object (const char *msg) const;
+
+private:
+ sarif_result *make_result_object (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind);
+ void set_any_logical_locs_arr (json::object *location_obj,
+ const logical_location *logical_loc);
+ json::object *make_location_object (const diagnostic_event &event);
+ json::object *
+ make_logical_location_object (const logical_location &logical_loc) const;
+ json::object *make_code_flow_object (const diagnostic_path &path);
+ json::object *make_thread_flow_object (const diagnostic_path &path);
+ json::object *
+ make_thread_flow_location_object (const diagnostic_event &event);
+ json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
+ json::object *maybe_make_physical_location_object (location_t loc);
+ json::object *make_artifact_location_object (location_t loc);
+ json::object *make_artifact_location_object (const char *filename);
+ json::object *make_artifact_location_object_for_pwd () const;
+ json::object *maybe_make_region_object (location_t loc) const;
+ json::object *maybe_make_region_object_for_context (location_t loc) const;
+ json::object *make_region_object_for_hint (const fixit_hint &hint) const;
+ json::object *make_multiformat_message_string (const char *msg) const;
+ json::object *make_top_level_object (json::array *results);
+ json::object *make_run_object (json::array *results);
+ json::object *make_tool_object () const;
+ json::object *make_driver_tool_component_object () const;
+ json::array *maybe_make_taxonomies_array () const;
+ json::object *maybe_make_cwe_taxonomy_object () const;
+ json::object *make_tool_component_reference_object_for_cwe () const;
+ json::object *
+ make_reporting_descriptor_object_for_warning (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind,
+ const char *option_text);
+ json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const;
+ json::object *
+ make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
+ json::object *make_artifact_object (const char *filename);
+ json::object *maybe_make_artifact_content_object (const char *filename) const;
+ json::object *maybe_make_artifact_content_object (const char *filename,
+ int start_line,
+ int end_line) const;
+ json::object *make_fix_object (const rich_location &rich_loc);
+ json::object *make_artifact_change_object (const rich_location &richloc);
+ json::object *make_replacement_object (const fixit_hint &hint) const;
+ json::object *make_artifact_content_object (const char *text) const;
+ int get_sarif_column (expanded_location exploc) const;
+
+ diagnostic_context *m_context;
+
+ /* The JSON array of pending diagnostics. */
+ json::array *m_results_array;
+
+ /* The JSON object for the result object (if any) in the current
+ diagnostic group. */
+ sarif_result *m_cur_group_result;
+
+ hash_set <const char *> m_filenames;
+ bool m_seen_any_relative_paths;
+ hash_set <free_string_hash> m_rule_id_set;
+ json::array *m_rules_arr;
+
+ /* The set of all CWE IDs we've seen, if any. */
+ hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
+
+ int m_tabstop;
+};
+
+static sarif_builder *the_builder;
+
+/* class sarif_result : public json::object. */
+
+/* Handle secondary diagnostics that occur within a diagnostic group.
+ The closest SARIF seems to have to nested diagnostics is the
+ "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
+ so we lazily set this property and populate the array if and when
+ secondary diagnostics occur (such as notes to a warning). */
+
+void
+sarif_result::on_nested_diagnostic (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t /*orig_diag_kind*/,
+ sarif_builder *builder)
+{
+ if (!m_related_locations_arr)
+ {
+ m_related_locations_arr = new json::array ();
+ set ("relatedLocations", m_related_locations_arr);
+ }
+
+ /* We don't yet generate meaningful logical locations for notes;
+ sometimes these will related to current_function_decl, but
+ often they won't. */
+ json::object *location_obj
+ = builder->make_location_object (*diagnostic->richloc, NULL);
+ json::object *message_obj
+ = builder->make_message_object (pp_formatted_text (context->printer));
+ pp_clear_output_area (context->printer);
+ location_obj->set ("message", message_obj);
+
+ m_related_locations_arr->append (location_obj);
+}
+
+/* class sarif_builder. */
+
+/* sarif_builder's ctor. */
+
+sarif_builder::sarif_builder (diagnostic_context *context)
+: m_context (context),
+ m_results_array (new json::array ()),
+ m_cur_group_result (NULL),
+ m_seen_any_relative_paths (false),
+ m_rule_id_set (),
+ m_rules_arr (new json::array ()),
+ m_tabstop (context->tabstop)
+{
+}
+
+/* Implementation of "end_diagnostic" for SARIF output. */
+
+void
+sarif_builder::end_diagnostic (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind)
+{
+
+ if (m_cur_group_result)
+ /* Nested diagnostic. */
+ m_cur_group_result->on_nested_diagnostic (context,
+ diagnostic,
+ orig_diag_kind,
+ this);
+ else
+ {
+ /* Top-level diagnostic. */
+ sarif_result *result_obj
+ = make_result_object (context, diagnostic, orig_diag_kind);
+ m_results_array->append (result_obj);
+ m_cur_group_result = result_obj;
+ }
+}
+
+/* Implementation of "end_group_cb" for SARIF output. */
+
+void
+sarif_builder::end_group ()
+{
+ m_cur_group_result = NULL;
+}
+
+/* Create a top-level object, and add it to all the results
+ (and other entities) we've seen so far.
+
+ Flush it all to OUTF. */
+
+void
+sarif_builder::flush_to_file (FILE *outf)
+{
+ json::object *top = make_top_level_object (m_results_array);
+ top->dump (outf);
+ m_results_array = NULL;
+ fprintf (outf, "\n");
+ delete top;
+}
+
+/* Attempt to convert DIAG_KIND to a suitable value for the "level"
+ property (SARIF v2.1.0 section 3.27.10).
+
+ Return NULL if there isn't one. */
+
+static const char *
+maybe_get_sarif_level (diagnostic_t diag_kind)
+{
+ switch (diag_kind)
+ {
+ case DK_WARNING:
+ return "warning";
+ case DK_ERROR:
+ return "error";
+ case DK_NOTE:
+ case DK_ANACHRONISM:
+ return "note";
+ default:
+ return NULL;
+ }
+}
+
+/* Make a string for DIAG_KIND suitable for use a ruleId
+ (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
+ have anything better to use. */
+
+static char *
+make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
+{
+ static const char *const diagnostic_kind_text[] = {
+#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
+#include "diagnostic.def"
+#undef DEFINE_DIAGNOSTIC_KIND
+ "must-not-happen"
+ };
+ /* Lose the trailing ": ". */
+ const char *kind_text = diagnostic_kind_text[diag_kind];
+ size_t len = strlen (kind_text);
+ gcc_assert (len > 2);
+ gcc_assert (kind_text[len - 2] == ':');
+ gcc_assert (kind_text[len - 1] == ' ');
+ char *rstrip = xstrdup (kind_text);
+ rstrip[len - 2] = '\0';
+ return rstrip;
+}
+
+/* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
+
+sarif_result *
+sarif_builder::make_result_object (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind)
+{
+ sarif_result *result_obj = new sarif_result ();
+
+ /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
+ /* Ideally we'd have an option_name for these. */
+ if (char *option_text
+ = context->option_name (context, diagnostic->option_index,
+ orig_diag_kind, diagnostic->kind))
+ {
+ /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
+ Set ruleId referencing them. */
+ result_obj->set ("ruleId", new json::string (option_text));
+ if (m_rule_id_set.contains (option_text))
+ free (option_text);
+ else
+ {
+ /* This is the first time we've seen this ruleId. */
+ /* Add to set, taking ownership. */
+ m_rule_id_set.add (option_text);
+
+ json::object *reporting_desc_obj
+ = make_reporting_descriptor_object_for_warning (context,
+ diagnostic,
+ orig_diag_kind,
+ option_text);
+ m_rules_arr->append (reporting_desc_obj);
+ }
+ }
+ else
+ {
+ /* Otherwise, we have an "error" or a stray "note"; use the
+ diagnostic kind as the ruleId, so that the result object at least
+ has a ruleId.
+ We don't bother creating reportingDescriptor objects for these. */
+ char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
+ result_obj->set ("ruleId", new json::string (rule_id));
+ free (rule_id);
+ }
+
+ /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
+ if (diagnostic->metadata)
+ if (int cwe_id = diagnostic->metadata->get_cwe ())
+ {
+ json::array *taxa_arr = new json::array ();
+ json::object *cwe_id_obj
+ = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
+ taxa_arr->append (cwe_id_obj);
+ result_obj->set ("taxa", taxa_arr);
+ }
+
+ /* "level" property (SARIF v2.1.0 section 3.27.10). */
+ if (const char *sarif_level = maybe_get_sarif_level (diagnostic->kind))
+ result_obj->set ("level", new json::string (sarif_level));
+
+ /* "message" property (SARIF v2.1.0 section 3.27.11). */
+ json::object *message_obj
+ = make_message_object (pp_formatted_text (context->printer));
+ pp_clear_output_area (context->printer);
+ result_obj->set ("message", message_obj);
+
+ /* "locations" property (SARIF v2.1.0 section 3.27.12). */
+ json::array *locations_arr = new json::array ();
+ const logical_location *logical_loc = NULL;
+ if (m_context->m_client_data_hooks)
+ logical_loc
+ = m_context->m_client_data_hooks->get_current_logical_location ();
+
+ json::object *location_obj
+ = make_location_object (*diagnostic->richloc, logical_loc);
+ locations_arr->append (location_obj);
+ result_obj->set ("locations", locations_arr);
+
+ /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
+ if (const diagnostic_path *path = diagnostic->richloc->get_path ())
+ {
+ json::array *code_flows_arr = new json::array ();
+ json::object *code_flow_obj = make_code_flow_object (*path);
+ code_flows_arr->append (code_flow_obj);
+ result_obj->set ("codeFlows", code_flows_arr);
+ }
+
+ /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
+ set up later, if any nested diagnostics occur within this diagnostic
+ group. */
+
+ /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
+ const rich_location *richloc = diagnostic->richloc;
+ if (richloc->get_num_fixit_hints ())
+ {
+ json::array *fix_arr = new json::array ();
+ json::object *fix_obj = make_fix_object (*richloc);
+ fix_arr->append (fix_obj);
+ result_obj->set ("fixes", fix_arr);
+ }
+
+ return result_obj;
+}
+
+/* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
+ for a GCC warning. */
+
+json::object *
+sarif_builder::
+make_reporting_descriptor_object_for_warning (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t /*orig_diag_kind*/,
+ const char *option_text)
+{
+ json::object *reporting_desc = new json::object ();
+
+ /* "id" property (SARIF v2.1.0 section 3.49.3). */
+ reporting_desc->set ("id", new json::string (option_text));
+
+ /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
+ it seems redundant compared to "id". */
+
+ /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
+ if (context->get_option_url)
+ {
+ char *option_url
+ = context->get_option_url (context, diagnostic->option_index);
+ if (option_url)
+ {
+ reporting_desc->set ("helpUri", new json::string (option_url));
+ free (option_url);
+ }
+ }
+
+ return reporting_desc;
+}
+
+/* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
+ for CWE_ID, for use within the CWE taxa array. */
+
+json::object *
+sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
+{
+ json::object *reporting_desc = new json::object ();
+
+ /* "id" property (SARIF v2.1.0 section 3.49.3). */
+ {
+ pretty_printer pp;
+ pp_printf (&pp, "%i", cwe_id);
+ reporting_desc->set ("id", new json::string (pp_formatted_text (&pp)));
+ }
+
+ /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
+ {
+ char *url = get_cwe_url (cwe_id);
+ reporting_desc->set ("helpUri", new json::string (url));
+ free (url);
+ }
+
+ return reporting_desc;
+}
+
+/* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
+ referencing CWE_ID, for use within a result object.
+ Also, add CWE_ID to m_cwe_id_set. */
+
+json::object *
+sarif_builder::
+make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
+{
+ json::object *desc_ref_obj = new json::object ();
+
+ /* "id" property (SARIF v2.1.0 section 3.52.4). */
+ {
+ pretty_printer pp;
+ pp_printf (&pp, "%i", cwe_id);
+ desc_ref_obj->set ("id", new json::string (pp_formatted_text (&pp)));
+ }
+
+ /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
+ json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
+ desc_ref_obj->set ("toolComponent", comp_ref_obj);
+
+ /* Add CWE_ID to our set. */
+ gcc_assert (cwe_id > 0);
+ m_cwe_id_set.add (cwe_id);
+
+ return desc_ref_obj;
+}
+
+/* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
+ references the CWE taxonomy. */
+
+json::object *
+sarif_builder::
+make_tool_component_reference_object_for_cwe () const
+{
+ json::object *comp_ref_obj = new json::object ();
+
+ /* "name" property (SARIF v2.1.0 section 3.54.3). */
+ comp_ref_obj->set ("name", new json::string ("cwe"));
+
+ return comp_ref_obj;
+}
+
+/* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
+ within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
+
+void
+sarif_builder::
+set_any_logical_locs_arr (json::object *location_obj,
+ const logical_location *logical_loc)
+{
+ if (!logical_loc)
+ return;
+ json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
+ json::array *location_locs_arr = new json::array ();
+ location_locs_arr->append (logical_loc_obj);
+ location_obj->set ("logicalLocations", location_locs_arr);
+}
+
+/* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
+ and LOGICAL_LOC. */
+
+json::object *
+sarif_builder::make_location_object (const rich_location &rich_loc,
+ const logical_location *logical_loc)
+{
+ json::object *location_obj = new json::object ();
+
+ /* Get primary loc from RICH_LOC. */
+ location_t loc = rich_loc.get_loc ();
+
+ /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
+ if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
+ location_obj->set ("physicalLocation", phs_loc_obj);
+
+ /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
+ set_any_logical_locs_arr (location_obj, logical_loc);
+
+ return location_obj;
+}
+
+/* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
+ within a diagnostic_path. */
+
+json::object *
+sarif_builder::make_location_object (const diagnostic_event &event)
+{
+ json::object *location_obj = new json::object ();
+
+ /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
+ location_t loc = event.get_location ();
+ if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
+ location_obj->set ("physicalLocation", phs_loc_obj);
+
+ /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
+ const logical_location *logical_loc = event.get_logical_location ();
+ set_any_logical_locs_arr (location_obj, logical_loc);
+
+ /* "message" property (SARIF v2.1.0 section 3.28.5). */
+ label_text ev_desc = event.get_desc (false);
+ json::object *message_obj = make_message_object (ev_desc.m_buffer);
+ location_obj->set ("message", message_obj);
+ ev_desc.maybe_free ();
+
+ return location_obj;
+}
+
+/* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
+ or return NULL;
+ Add any filename to the m_artifacts. */
+
+json::object *
+sarif_builder::maybe_make_physical_location_object (location_t loc)
+{
+ if (loc <= BUILTINS_LOCATION)
+ return NULL;
+
+ json::object *phys_loc_obj = new json::object ();
+
+ /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
+ json::object *artifact_loc_obj = make_artifact_location_object (loc);
+ phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
+ m_filenames.add (LOCATION_FILE (loc));
+
+ /* "region" property (SARIF v2.1.0 section 3.29.4). */
+ if (json::object *region_obj = maybe_make_region_object (loc))
+ phys_loc_obj->set ("region", region_obj);
+
+ /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
+ if (json::object *context_region_obj
+ = maybe_make_region_object_for_context (loc))
+ phys_loc_obj->set ("contextRegion", context_region_obj);
+
+ /* Instead, we add artifacts to the run as a whole,
+ with artifact.contents.
+ Could do both, though. */
+
+ return phys_loc_obj;
+}
+
+/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
+ or return NULL. */
+
+json::object *
+sarif_builder::make_artifact_location_object (location_t loc)
+{
+ return make_artifact_location_object (LOCATION_FILE (loc));
+}
+
+/* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
+ for when we need to express paths relative to PWD. */
+
+#define PWD_PROPERTY_NAME ("PWD")
+
+/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
+ or return NULL. */
+
+json::object *
+sarif_builder::make_artifact_location_object (const char *filename)
+{
+ json::object *artifact_loc_obj = new json::object ();
+
+ /* "uri" property (SARIF v2.1.0 section 3.4.3). */
+ artifact_loc_obj->set ("uri", new json::string (filename));
+
+ if (filename[0] != '/')
+ {
+ /* If we have a relative path, set the "uriBaseId" property
+ (SARIF v2.1.0 section 3.4.4). */
+ artifact_loc_obj->set ("uriBaseId", new json::string (PWD_PROPERTY_NAME));
+ m_seen_any_relative_paths = true;
+ }
+
+ return artifact_loc_obj;
+}
+
+/* Get the PWD, or NULL, as an absolute file-based URI,
+ adding a trailing forward slash (as required by SARIF v2.1.0
+ section 3.14.14). */
+
+static char *
+make_pwd_uri_str ()
+{
+ /* The prefix of a file-based URI, up to, but not including the path. */
+#define FILE_PREFIX ("file://")
+
+ const char *pwd = getpwd ();
+ if (!pwd)
+ return NULL;
+ size_t len = strlen (pwd);
+ if (len == 0 || pwd[len - 1] != '/')
+ return concat (FILE_PREFIX, pwd, "/", NULL);
+ else
+ {
+ gcc_assert (pwd[len - 1] == '/');
+ return concat (FILE_PREFIX, pwd, NULL);
+ }
+}
+
+/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
+ for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
+ section 3.14.14) when we have any relative paths. */
+
+json::object *
+sarif_builder::make_artifact_location_object_for_pwd () const
+{
+ json::object *artifact_loc_obj = new json::object ();
+
+ /* "uri" property (SARIF v2.1.0 section 3.4.3). */
+ if (char *pwd = make_pwd_uri_str ())
+ {
+ gcc_assert (strlen (pwd) > 0);
+ gcc_assert (pwd[strlen (pwd) - 1] == '/');
+ artifact_loc_obj->set ("uri", new json::string (pwd));
+ free (pwd);
+ }
+
+ return artifact_loc_obj;
+}
+
+/* Get the column number within EXPLOC. */
+
+int
+sarif_builder::get_sarif_column (expanded_location exploc) const
+{
+ cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
+ return location_compute_display_column (exploc, policy);
+}
+
+/* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
+ or return NULL. */
+
+json::object *
+sarif_builder::maybe_make_region_object (location_t loc) const
+{
+ location_t caret_loc = get_pure_location (loc);
+
+ if (caret_loc <= BUILTINS_LOCATION)
+ return NULL;
+
+ location_t start_loc = get_start (loc);
+ location_t finish_loc = get_finish (loc);
+
+ expanded_location exploc_caret = expand_location (caret_loc);
+ expanded_location exploc_start = expand_location (start_loc);
+ expanded_location exploc_finish = expand_location (finish_loc);
+
+ if (exploc_start.file !=exploc_caret.file)
+ return NULL;
+ if (exploc_finish.file !=exploc_caret.file)
+ return NULL;
+
+ json::object *region_obj = new json::object ();
+
+ /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
+ region_obj->set ("startLine", new json::integer_number (exploc_start.line));
+
+ /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
+ region_obj->set ("startColumn",
+ new json::integer_number (get_sarif_column (exploc_start)));
+
+ /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
+ if (exploc_finish.line != exploc_start.line)
+ region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
+
+ /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
+ This expresses the column immediately beyond the range. */
+ {
+ int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
+ region_obj->set ("endColumn", new json::integer_number (next_column));
+ }
+
+ return region_obj;
+}
+
+/* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
+ property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
+
+ This is similar to maybe_make_region_object, but ignores column numbers,
+ covering the line(s) as a whole, and including a "snippet" property
+ embedding those source lines, making it easier for consumers to show
+ the pertinent source. */
+
+json::object *
+sarif_builder::maybe_make_region_object_for_context (location_t loc) const
+{
+ location_t caret_loc = get_pure_location (loc);
+
+ if (caret_loc <= BUILTINS_LOCATION)
+ return NULL;
+
+ location_t start_loc = get_start (loc);
+ location_t finish_loc = get_finish (loc);
+
+ expanded_location exploc_caret = expand_location (caret_loc);
+ expanded_location exploc_start = expand_location (start_loc);
+ expanded_location exploc_finish = expand_location (finish_loc);
+
+ if (exploc_start.file !=exploc_caret.file)
+ return NULL;
+ if (exploc_finish.file !=exploc_caret.file)
+ return NULL;
+
+ json::object *region_obj = new json::object ();
+
+ /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
+ region_obj->set ("startLine", new json::integer_number (exploc_start.line));
+
+ /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
+ if (exploc_finish.line != exploc_start.line)
+ region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
+
+ /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
+ if (json::object *artifact_content_obj
+ = maybe_make_artifact_content_object (exploc_start.file,
+ exploc_start.line,
+ exploc_finish.line))
+ region_obj->set ("snippet", artifact_content_obj);
+
+ return region_obj;
+}
+
+/* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
+ of HINT (as per SARIF v2.1.0 section 3.57.3). */
+
+json::object *
+sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
+{
+ location_t start_loc = hint.get_start_loc ();
+ location_t next_loc = hint.get_next_loc ();
+
+ expanded_location exploc_start = expand_location (start_loc);
+ expanded_location exploc_next = expand_location (next_loc);
+
+ json::object *region_obj = new json::object ();
+
+ /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
+ region_obj->set ("startLine", new json::integer_number (exploc_start.line));
+
+ /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
+ int start_col = get_sarif_column (exploc_start);
+ region_obj->set ("startColumn",
+ new json::integer_number (start_col));
+
+ /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
+ if (exploc_next.line != exploc_start.line)
+ region_obj->set ("endLine", new json::integer_number (exploc_next.line));
+
+ /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
+ This expresses the column immediately beyond the range. */
+ int next_col = get_sarif_column (exploc_next);
+ region_obj->set ("endColumn", new json::integer_number (next_col));
+
+ return region_obj;
+}
+
+/* Attempt to get a string for a logicalLocation's "kind" property
+ (SARIF v2.1.0 section 3.33.7).
+ Return NULL if unknown. */
+
+static const char *
+maybe_get_sarif_kind (enum logical_location_kind kind)
+{
+ switch (kind)
+ {
+ default:
+ gcc_unreachable ();
+ case LOGICAL_LOCATION_KIND_UNKNOWN:
+ return NULL;
+
+ case LOGICAL_LOCATION_KIND_FUNCTION:
+ return "function";
+ case LOGICAL_LOCATION_KIND_MEMBER:
+ return "member";
+ case LOGICAL_LOCATION_KIND_MODULE:
+ return "module";
+ case LOGICAL_LOCATION_KIND_NAMESPACE:
+ return "namespace";
+ case LOGICAL_LOCATION_KIND_TYPE:
+ return "type";
+ case LOGICAL_LOCATION_KIND_RETURN_TYPE:
+ return "returnType";
+ case LOGICAL_LOCATION_KIND_PARAMETER:
+ return "parameter";
+ case LOGICAL_LOCATION_KIND_VARIABLE:
+ return "variable";
+ }
+}
+
+/* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
+ or return NULL. */
+
+json::object *
+sarif_builder::
+make_logical_location_object (const logical_location &logical_loc) const
+{
+ json::object *logical_loc_obj = new json::object ();
+
+ /* "name" property (SARIF v2.1.0 section 3.33.4). */
+ if (const char *short_name = logical_loc.get_short_name ())
+ logical_loc_obj->set ("name", new json::string (short_name));
+
+ /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
+ if (const char *name_with_scope = logical_loc.get_name_with_scope ())
+ logical_loc_obj->set ("fullyQualifiedName",
+ new json::string (name_with_scope));
+
+ /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
+ if (const char *internal_name = logical_loc.get_internal_name ())
+ logical_loc_obj->set ("decoratedName", new json::string (internal_name));
+
+ /* "kind" property (SARIF v2.1.0 section 3.33.7). */
+ enum logical_location_kind kind = logical_loc.get_kind ();
+ if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
+ logical_loc_obj->set ("kind", new json::string (sarif_kind_str));
+
+ return logical_loc_obj;
+}
+
+/* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
+
+json::object *
+sarif_builder::make_code_flow_object (const diagnostic_path &path)
+{
+ json::object *code_flow_obj = new json::object ();
+
+ /* "threadFlows" property (SARIF v2.1.0 section 3.36.3).
+ Currently we only support one thread per result. */
+ json::array *thread_flows_arr = new json::array ();
+ json::object *thread_flow_obj = make_thread_flow_object (path);
+ thread_flows_arr->append (thread_flow_obj);
+ code_flow_obj->set ("threadFlows", thread_flows_arr);
+
+ return code_flow_obj;
+}
+
+/* Make a threadFlow object (SARIF v2.1.0 section 3.37) for PATH. */
+
+json::object *
+sarif_builder::make_thread_flow_object (const diagnostic_path &path)
+{
+ json::object *thread_flow_obj = new json::object ();
+
+ /* "locations" property (SARIF v2.1.0 section 3.37.6). */
+ json::array *locations_arr = new json::array ();
+ for (unsigned i = 0; i < path.num_events (); i++)
+ {
+ const diagnostic_event &event = path.get_event (i);
+ json::object *thread_flow_loc_obj
+ = make_thread_flow_location_object (event);
+ locations_arr->append (thread_flow_loc_obj);
+ }
+ thread_flow_obj->set ("locations", locations_arr);
+
+ return thread_flow_obj;
+}
+
+/* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
+
+json::object *
+sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev)
+{
+ json::object *thread_flow_loc_obj = new json::object ();
+
+ /* "location" property (SARIF v2.1.0 section 3.38.3). */
+ json::object *location_obj = make_location_object (ev);
+ thread_flow_loc_obj->set ("location", location_obj);
+
+ /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
+ diagnostic_event::meaning m = ev.get_meaning ();
+ if (json::array *kinds_arr = maybe_make_kinds_array (m))
+ thread_flow_loc_obj->set ("kinds", kinds_arr);
+
+ /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
+ thread_flow_loc_obj->set ("nestingLevel",
+ new json::integer_number (ev.get_stack_depth ()));
+
+ /* It might be nice to eventually implement the following for -fanalyzer:
+ - the "stack" property (SARIF v2.1.0 section 3.38.5)
+ - the "state" property (SARIF v2.1.0 section 3.38.9)
+ - the "importance" property (SARIF v2.1.0 section 3.38.13). */
+
+ return thread_flow_loc_obj;
+}
+
+/* If M has any known meaning, make a json array suitable for the "kinds"
+ property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
+
+ Otherwise, return NULL. */
+
+json::array *
+sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
+{
+ if (m.m_verb == diagnostic_event::VERB_unknown
+ && m.m_noun == diagnostic_event::NOUN_unknown
+ && m.m_property == diagnostic_event::PROPERTY_unknown)
+ return NULL;
+
+ json::array *kinds_arr = new json::array ();
+ if (const char *verb_str
+ = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
+ kinds_arr->append (new json::string (verb_str));
+ if (const char *noun_str
+ = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
+ kinds_arr->append (new json::string (noun_str));
+ if (const char *property_str
+ = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
+ kinds_arr->append (new json::string (property_str));
+ return kinds_arr;
+}
+
+/* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
+
+json::object *
+sarif_builder::make_message_object (const char *msg) const
+{
+ json::object *message_obj = new json::object ();
+
+ /* "text" property (SARIF v2.1.0 section 3.11.8). */
+ message_obj->set ("text", new json::string (msg));
+
+ return message_obj;
+}
+
+/* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
+ for MSG. */
+
+json::object *
+sarif_builder::make_multiformat_message_string (const char *msg) const
+{
+ json::object *message_obj = new json::object ();
+
+ /* "text" property (SARIF v2.1.0 section 3.12.3). */
+ message_obj->set ("text", new json::string (msg));
+
+ return message_obj;
+}
+
+#define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
+#define SARIF_VERSION "2.1.0"
+
+/* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
+ Take ownership of RESULTS. */
+
+json::object *
+sarif_builder::make_top_level_object (json::array *results)
+{
+ json::object *log_obj = new json::object ();
+
+ /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
+ log_obj->set ("$schema", new json::string (SARIF_SCHEMA));
+
+ /* "version" property (SARIF v2.1.0 section 3.13.2). */
+ log_obj->set ("version", new json::string (SARIF_VERSION));
+
+ /* "runs" property (SARIF v2.1.0 section 3.13.4). */
+ json::array *run_arr = new json::array ();
+ json::object *run_obj = make_run_object (results);
+ run_arr->append (run_obj);
+ log_obj->set ("runs", run_arr);
+
+ return log_obj;
+}
+
+/* Make a run object (SARIF v2.1.0 section 3.14).
+ Take ownership of RESULTS. */
+
+json::object *
+sarif_builder::make_run_object (json::array *results)
+{
+ json::object *run_obj = new json::object ();
+
+ /* "tool" property (SARIF v2.1.0 section 3.14.6). */
+ json::object *tool_obj = make_tool_object ();
+ run_obj->set ("tool", tool_obj);
+
+ /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
+ if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
+ run_obj->set ("taxonomies", taxonomies_arr);
+
+ /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
+ if (m_seen_any_relative_paths)
+ {
+ json::object *orig_uri_base_ids = new json::object ();
+ run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
+ json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
+ orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
+ }
+
+ /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
+ json::array *artifacts_arr = new json::array ();
+ for (auto iter : m_filenames)
+ {
+ json::object *artifact_obj = make_artifact_object (iter);
+ artifacts_arr->append (artifact_obj);
+ }
+ run_obj->set ("artifacts", artifacts_arr);
+
+ /* "results" property (SARIF v2.1.0 section 3.14.23). */
+ run_obj->set ("results", results);
+
+ return run_obj;
+}
+
+/* Make a tool object (SARIF v2.1.0 section 3.18). */
+
+json::object *
+sarif_builder::make_tool_object () const
+{
+ json::object *tool_obj = new json::object ();
+
+ /* "driver" property (SARIF v2.1.0 section 3.18.2). */
+ json::object *driver_obj = make_driver_tool_component_object ();
+ tool_obj->set ("driver", driver_obj);
+
+ /* Report plugins via the "extensions" property
+ (SARIF v2.1.0 section 3.18.3). */
+ if (m_context->m_client_data_hooks)
+ if (const client_version_info *vinfo
+ = m_context->m_client_data_hooks->get_any_version_info ())
+ {
+ class my_plugin_visitor : public client_version_info :: plugin_visitor
+ {
+ public:
+ void on_plugin (const diagnostic_client_plugin_info &p) final override
+ {
+ /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
+ for the plugin. */
+ json::object *plugin_obj = new json::object ();
+ m_plugin_objs.safe_push (plugin_obj);
+
+ /* "name" property (SARIF v2.1.0 section 3.19.8). */
+ if (const char *short_name = p.get_short_name ())
+ plugin_obj->set ("name", new json::string (short_name));
+
+ /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
+ if (const char *full_name = p.get_full_name ())
+ plugin_obj->set ("fullName", new json::string (full_name));
+
+ /* "version" property (SARIF v2.1.0 section 3.19.13). */
+ if (const char *version = p.get_version ())
+ plugin_obj->set ("version", new json::string (version));
+ }
+ auto_vec <json::object *> m_plugin_objs;
+ };
+ my_plugin_visitor v;
+ vinfo->for_each_plugin (v);
+ if (v.m_plugin_objs.length () > 0)
+ {
+ json::array *extensions_arr = new json::array ();
+ tool_obj->set ("extensions", extensions_arr);
+ for (auto iter : v.m_plugin_objs)
+ extensions_arr->append (iter);
+ }
+ }
+
+ /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
+ "extensions" (see toplev.cc: print_version). */
+
+ return tool_obj;
+}
+
+/* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
+ calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
+
+json::object *
+sarif_builder::make_driver_tool_component_object () const
+{
+ json::object *driver_obj = new json::object ();
+
+ if (m_context->m_client_data_hooks)
+ if (const client_version_info *vinfo
+ = m_context->m_client_data_hooks->get_any_version_info ())
+ {
+ /* "name" property (SARIF v2.1.0 section 3.19.8). */
+ if (const char *name = vinfo->get_tool_name ())
+ driver_obj->set ("name", new json::string (name));
+
+ /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
+ if (char *full_name = vinfo->maybe_make_full_name ())
+ {
+ driver_obj->set ("fullName", new json::string (full_name));
+ free (full_name);
+ }
+
+ /* "version" property (SARIF v2.1.0 section 3.19.13). */
+ if (const char *version = vinfo->get_version_string ())
+ driver_obj->set ("version", new json::string (version));
+
+ /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
+ if (char *version_url = vinfo->maybe_make_version_url ())
+ {
+ driver_obj->set ("informationUri", new json::string (version_url));
+ free (version_url);
+ }
+ }
+
+ /* "rules" property (SARIF v2.1.0 section 3.19.23). */
+ driver_obj->set ("rules", m_rules_arr);
+
+ return driver_obj;
+}
+
+/* If we've seen any CWE IDs, make an array for the "taxonomies" property
+ (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
+ toolComponent (3.19) as per 3.19.3, representing the CWE.
+
+ Otherwise return NULL. */
+
+json::array *
+sarif_builder::maybe_make_taxonomies_array () const
+{
+ json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
+ if (!cwe_obj)
+ return NULL;
+
+ /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
+ json::array *taxonomies_arr = new json::array ();
+ taxonomies_arr->append (cwe_obj);
+ return taxonomies_arr;
+}
+
+/* If we've seen any CWE IDs, make a toolComponent object
+ (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
+ Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
+
+ Otherwise return NULL. */
+
+json::object *
+sarif_builder::maybe_make_cwe_taxonomy_object () const
+{
+ if (m_cwe_id_set.is_empty ())
+ return NULL;
+
+ json::object *taxonomy_obj = new json::object ();
+
+ /* "name" property (SARIF v2.1.0 section 3.19.8). */
+ taxonomy_obj->set ("name", new json::string ("CWE"));
+
+ /* "version" property (SARIF v2.1.0 section 3.19.13). */
+ taxonomy_obj->set ("version", new json::string ("4.7"));
+
+ /* "organization" property (SARIF v2.1.0 section 3.19.18). */
+ taxonomy_obj->set ("organization", new json::string ("MITRE"));
+
+ /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
+ json::object *short_desc
+ = make_multiformat_message_string ("The MITRE"
+ " Common Weakness Enumeration");
+ taxonomy_obj->set ("shortDescription", short_desc);
+
+ /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
+ json::array *taxa_arr = new json::array ();
+ for (auto cwe_id : m_cwe_id_set)
+ {
+ json::object *cwe_taxon
+ = make_reporting_descriptor_object_for_cwe_id (cwe_id);
+ taxa_arr->append (cwe_taxon);
+ }
+ taxonomy_obj->set ("taxa", taxa_arr);
+
+ return taxonomy_obj;
+}
+
+/* Make an artifact object (SARIF v2.1.0 section 3.24). */
+
+json::object *
+sarif_builder::make_artifact_object (const char *filename)
+{
+ json::object *artifact_obj = new json::object ();
+
+ /* "location" property (SARIF v2.1.0 section 3.24.2). */
+ json::object *artifact_loc_obj = make_artifact_location_object (filename);
+ artifact_obj->set ("location", artifact_loc_obj);
+
+ /* "contents" property (SARIF v2.1.0 section 3.24.8). */
+ if (json::object *artifact_content_obj
+ = maybe_make_artifact_content_object (filename))
+ artifact_obj->set ("contents", artifact_content_obj);
+
+ /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
+ if (m_context->m_client_data_hooks)
+ if (const char *source_lang
+ = m_context->m_client_data_hooks->maybe_get_sarif_source_language
+ (filename))
+ artifact_obj->set ("sourceLanguage", new json::string (source_lang));
+
+ return artifact_obj;
+}
+
+/* Read all data from F_IN until EOF.
+ Return a NULL-terminated buffer containing the data, which must be
+ freed by the caller.
+ Return NULL on errors. */
+
+static char *
+read_until_eof (FILE *f_in)
+{
+ /* Read content, allocating a buffer for it. */
+ char *result = NULL;
+ size_t total_sz = 0;
+ size_t alloc_sz = 0;
+ char buf[4096];
+ size_t iter_sz_in;
+
+ while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
+ {
+ gcc_assert (alloc_sz >= total_sz);
+ size_t old_total_sz = total_sz;
+ total_sz += iter_sz_in;
+ /* Allow 1 extra byte for 0-termination. */
+ if (alloc_sz < (total_sz + 1))
+ {
+ size_t new_alloc_sz = alloc_sz ? alloc_sz * 2: total_sz + 1;
+ result = (char *)xrealloc (result, new_alloc_sz);
+ alloc_sz = new_alloc_sz;
+ }
+ memcpy (result + old_total_sz, buf, iter_sz_in);
+ }
+
+ if (!feof (f_in))
+ return NULL;
+
+ /* 0-terminate the buffer. */
+ gcc_assert (total_sz < alloc_sz);
+ result[total_sz] = '\0';
+
+ return result;
+}
+
+/* Read all data from FILENAME until EOF.
+ Return a NULL-terminated buffer containing the data, which must be
+ freed by the caller.
+ Return NULL on errors. */
+
+static char *
+maybe_read_file (const char *filename)
+{
+ FILE *f_in = fopen (filename, "r");
+ if (!f_in)
+ return NULL;
+ char *result = read_until_eof (f_in);
+ fclose (f_in);
+ return result;
+}
+
+/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
+ full contents of FILENAME. */
+
+json::object *
+sarif_builder::maybe_make_artifact_content_object (const char *filename) const
+{
+ char *text_utf8 = maybe_read_file (filename);
+ if (!text_utf8)
+ return NULL;
+
+ json::object *artifact_content_obj = new json::object ();
+ artifact_content_obj->set ("text", new json::string (text_utf8));
+ free (text_utf8);
+
+ return artifact_content_obj;
+}
+
+/* Attempt to read the given range of lines from FILENAME; return
+ a freshly-allocated 0-terminated buffer containing them, or NULL. */
+
+static char *
+get_source_lines (const char *filename,
+ int start_line,
+ int end_line)
+{
+ auto_vec<char> result;
+
+ for (int line = start_line; line <= end_line; line++)
+ {
+ char_span line_content = location_get_source_line (filename, line);
+ if (!line_content.get_buffer ())
+ return NULL;
+ result.reserve (line_content.length () + 1);
+ for (size_t i = 0; i < line_content.length (); i++)
+ result.quick_push (line_content[i]);
+ result.quick_push ('\n');
+ }
+ result.safe_push ('\0');
+
+ return xstrdup (result.address ());
+}
+
+/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
+ run of lines within FILENAME (including the endpoints). */
+
+json::object *
+sarif_builder::maybe_make_artifact_content_object (const char *filename,
+ int start_line,
+ int end_line) const
+{
+ char *text_utf8 = get_source_lines (filename, start_line, end_line);
+
+ if (!text_utf8)
+ return NULL;
+
+ json::object *artifact_content_obj = new json::object ();
+ artifact_content_obj->set ("text", new json::string (text_utf8));
+ free (text_utf8);
+
+ return artifact_content_obj;
+}
+
+/* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
+
+json::object *
+sarif_builder::make_fix_object (const rich_location &richloc)
+{
+ json::object *fix_obj = new json::object ();
+
+ /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
+ /* We assume that all fix-it hints in RICHLOC affect the same file. */
+ json::array *artifact_change_arr = new json::array ();
+ json::object *artifact_change_obj = make_artifact_change_object (richloc);
+ artifact_change_arr->append (artifact_change_obj);
+ fix_obj->set ("artifactChanges", artifact_change_arr);
+
+ return fix_obj;
+}
+
+/* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
+
+json::object *
+sarif_builder::make_artifact_change_object (const rich_location &richloc)
+{
+ json::object *artifact_change_obj = new json::object ();
+
+ /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
+ json::object *artifact_location_obj
+ = make_artifact_location_object (richloc.get_loc ());
+ artifact_change_obj->set ("artifactLocation", artifact_location_obj);
+
+ /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
+ json::array *replacement_arr = new json::array ();
+ for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
+ {
+ const fixit_hint *hint = richloc.get_fixit_hint (i);
+ json::object *replacement_obj = make_replacement_object (*hint);
+ replacement_arr->append (replacement_obj);
+ }
+ artifact_change_obj->set ("replacements", replacement_arr);
+
+ return artifact_change_obj;
+}
+
+/* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
+
+json::object *
+sarif_builder::make_replacement_object (const fixit_hint &hint) const
+{
+ json::object *replacement_obj = new json::object ();
+
+ /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
+ json::object *region_obj = make_region_object_for_hint (hint);
+ replacement_obj->set ("deletedRegion", region_obj);
+
+ /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
+ json::object *content_obj = make_artifact_content_object (hint.get_string ());
+ replacement_obj->set ("insertedContent", content_obj);
+
+ return replacement_obj;
+}
+
+/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
+
+json::object *
+sarif_builder::make_artifact_content_object (const char *text) const
+{
+ json::object *content_obj = new json::object ();
+
+ /* "text" property (SARIF v2.1.0 section 3.3.2). */
+ content_obj->set ("text", new json::string (text));
+
+ return content_obj;
+}
+
+/* No-op implementation of "begin_diagnostic" for SARIF output. */
+
+static void
+sarif_begin_diagnostic (diagnostic_context *, diagnostic_info *)
+{
+}
+
+/* Implementation of "end_diagnostic" for SARIF output. */
+
+static void
+sarif_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind)
+{
+ gcc_assert (the_builder);
+ the_builder->end_diagnostic (context, diagnostic, orig_diag_kind);
+}
+
+/* No-op implementation of "begin_group_cb" for SARIF output. */
+
+static void
+sarif_begin_group (diagnostic_context *)
+{
+}
+
+/* Implementation of "end_group_cb" for SARIF output. */
+
+static void
+sarif_end_group (diagnostic_context *)
+{
+ gcc_assert (the_builder);
+ the_builder->end_group ();
+}
+
+/* Flush the top-level array to OUTF. */
+
+static void
+sarif_flush_to_file (FILE *outf)
+{
+ gcc_assert (the_builder);
+ the_builder->flush_to_file (outf);
+ delete the_builder;
+ the_builder = NULL;
+}
+
+/* Callback for final cleanup for SARIF output to stderr. */
+
+static void
+sarif_stderr_final_cb (diagnostic_context *)
+{
+ gcc_assert (the_builder);
+ sarif_flush_to_file (stderr);
+}
+
+static char *sarif_output_base_file_name;
+
+/* Callback for final cleanup for SARIF output to a file. */
+
+static void
+sarif_file_final_cb (diagnostic_context *)
+{
+ char *filename = concat (sarif_output_base_file_name, ".sarif", NULL);
+ FILE *outf = fopen (filename, "w");
+ if (!outf)
+ {
+ const char *errstr = xstrerror (errno);
+ fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
+ filename, errstr);
+ free (filename);
+ return;
+ }
+ gcc_assert (the_builder);
+ sarif_flush_to_file (outf);
+ fclose (outf);
+ free (filename);
+}
+
+/* Populate CONTEXT in preparation for SARIF output (either to stderr, or
+ to a file). */
+
+static void
+diagnostic_output_format_init_sarif (diagnostic_context *context)
+{
+ the_builder = new sarif_builder (context);
+
+ /* Override callbacks. */
+ context->begin_diagnostic = sarif_begin_diagnostic;
+ context->end_diagnostic = sarif_end_diagnostic;
+ context->begin_group_cb = sarif_begin_group;
+ context->end_group_cb = sarif_end_group;
+ context->print_path = NULL; /* handled in sarif_end_diagnostic. */
+
+ /* The metadata is handled in SARIF format, rather than as text. */
+ context->show_cwe = false;
+
+ /* The option is handled in SARIF format, rather than as text. */
+ context->show_option_requested = false;
+
+ /* Don't colorize the text. */
+ pp_show_color (context->printer) = false;
+}
+
+/* Populate CONTEXT in preparation for SARIF output to stderr. */
+
+void
+diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
+{
+ diagnostic_output_format_init_sarif (context);
+ context->final_cb = sarif_stderr_final_cb;
+}
+
+/* Populate CONTEXT in preparation for SARIF output to a file named
+ BASE_FILE_NAME.sarif. */
+
+void
+diagnostic_output_format_init_sarif_file (diagnostic_context *context,
+ const char *base_file_name)
+{
+ diagnostic_output_format_init_sarif (context);
+ context->final_cb = sarif_file_final_cb;
+ sarif_output_base_file_name = xstrdup (base_file_name);
+}