diff options
Diffstat (limited to 'gcc/diagnostic-format-sarif.cc')
-rw-r--r-- | gcc/diagnostic-format-sarif.cc | 1586 |
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); +} |