mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-21 19:35:28 -05:00
Existing text output in GCC has to be implemented by writing sequentially to a pretty_printer instance. This makes it hard to implement some kinds of diagnostic output (see e.g. diagnostic-show-locus.cc). This patch adds more flexible ways of creating text output: - a canvas class, which can be "painted" to via random-access (rather that sequentially) - a table class for 2D grid layout, supporting items that span multiple rows/columns - a widget class for organizing diagrams hierarchically. The patch also expands GCC's diagnostics subsystem so that diagnostics can have "text art" diagrams - think ASCII art, but potentially including some Unicode characters, such as box-drawing chars. The new code is in a new "gcc/text-art" subdirectory and "text_art" namespace. The patch adds a new "-fdiagnostics-text-art-charset=VAL" option, with values: - "none": don't emit diagrams (added to -fdiagnostics-plain-output) - "ascii": use pure ASCII in diagrams - "unicode": allow for conservative use of unicode drawing characters (such as box-drawing characters). - "emoji" (the default): as "unicode", but potentially allow for conservative use of emoji in the output (such as U+26A0 WARNING SIGN). I made it possible to disable emoji separately from unicode as I believe there's a generation gap in acceptance of these characters (some older programmers have a visceral reaction against them, whereas younger programmers may have no problem with them). Diagrams are emitted to stderr by default. With SARIF output they are captured as a location in "relatedLocations", with the diagram as a code block in Markdown within a "markdown" property of a message. This patch doesn't add any such diagram usage to GCC, saving that for followups, apart from adding a plugin to the test suite to exercise the functionality. contrib/ChangeLog: * unicode/gen-box-drawing-chars.py: New file. * unicode/gen-combining-chars.py: New file. * unicode/gen-printable-chars.py: New file. gcc/ChangeLog: * Makefile.in (OBJS-libcommon): Add text-art/box-drawing.o, text-art/canvas.o, text-art/ruler.o, text-art/selftests.o, text-art/style.o, text-art/styled-string.o, text-art/table.o, text-art/theme.o, and text-art/widget.o. * color-macros.h (COLOR_FG_BRIGHT_BLACK): New. (COLOR_FG_BRIGHT_RED): New. (COLOR_FG_BRIGHT_GREEN): New. (COLOR_FG_BRIGHT_YELLOW): New. (COLOR_FG_BRIGHT_BLUE): New. (COLOR_FG_BRIGHT_MAGENTA): New. (COLOR_FG_BRIGHT_CYAN): New. (COLOR_FG_BRIGHT_WHITE): New. (COLOR_BG_BRIGHT_BLACK): New. (COLOR_BG_BRIGHT_RED): New. (COLOR_BG_BRIGHT_GREEN): New. (COLOR_BG_BRIGHT_YELLOW): New. (COLOR_BG_BRIGHT_BLUE): New. (COLOR_BG_BRIGHT_MAGENTA): New. (COLOR_BG_BRIGHT_CYAN): New. (COLOR_BG_BRIGHT_WHITE): New. * common.opt (fdiagnostics-text-art-charset=): New option. (diagnostic-text-art.h): New SourceInclude. (diagnostic_text_art_charset) New Enum and EnumValues. * configure: Regenerate. * configure.ac (gccdepdir): Add text-art to loop. * diagnostic-diagram.h: New file. * diagnostic-format-json.cc (json_emit_diagram): New. (diagnostic_output_format_init_json): Wire it up to context->m_diagrams.m_emission_cb. * diagnostic-format-sarif.cc: Include "diagnostic-diagram.h" and "text-art/canvas.h". (sarif_result::on_nested_diagnostic): Move code to... (sarif_result::add_related_location): ...this new function. (sarif_result::on_diagram): New. (sarif_builder::emit_diagram): New. (sarif_builder::make_message_object_for_diagram): New. (sarif_emit_diagram): New. (diagnostic_output_format_init_sarif): Set context->m_diagrams.m_emission_cb to sarif_emit_diagram. * diagnostic-text-art.h: New file. * diagnostic.cc: Include "diagnostic-text-art.h", "diagnostic-diagram.h", and "text-art/theme.h". (diagnostic_initialize): Initialize context->m_diagrams and call diagnostics_text_art_charset_init. (diagnostic_finish): Clean up context->m_diagrams.m_theme. (diagnostic_emit_diagram): New. (diagnostics_text_art_charset_init): New. * diagnostic.h (text_art::theme): New forward decl. (class diagnostic_diagram): Likewise. (diagnostic_context::m_diagrams): New field. (diagnostic_emit_diagram): New decl. * doc/invoke.texi (Diagnostic Message Formatting Options): Add -fdiagnostics-text-art-charset=. (-fdiagnostics-plain-output): Add -fdiagnostics-text-art-charset=none. * gcc.cc: Include "diagnostic-text-art.h". (driver_handle_option): Handle OPT_fdiagnostics_text_art_charset_. * opts-common.cc (decode_cmdline_options_to_array): Add "-fdiagnostics-text-art-charset=none" to expanded_args for -fdiagnostics-plain-output. * opts.cc: Include "diagnostic-text-art.h". (common_handle_option): Handle OPT_fdiagnostics_text_art_charset_. * pretty-print.cc (pp_unicode_character): New. * pretty-print.h (pp_unicode_character): New decl. * selftest-run-tests.cc: Include "text-art/selftests.h". (selftest::run_tests): Call text_art_tests. * text-art/box-drawing-chars.inc: New file, generated by contrib/unicode/gen-box-drawing-chars.py. * text-art/box-drawing.cc: New file. * text-art/box-drawing.h: New file. * text-art/canvas.cc: New file. * text-art/canvas.h: New file. * text-art/ruler.cc: New file. * text-art/ruler.h: New file. * text-art/selftests.cc: New file. * text-art/selftests.h: New file. * text-art/style.cc: New file. * text-art/styled-string.cc: New file. * text-art/table.cc: New file. * text-art/table.h: New file. * text-art/theme.cc: New file. * text-art/theme.h: New file. * text-art/types.h: New file. * text-art/widget.cc: New file. * text-art/widget.h: New file. gcc/testsuite/ChangeLog: * gcc.dg/plugin/diagnostic-test-text-art-ascii-bw.c: New test. * gcc.dg/plugin/diagnostic-test-text-art-ascii-color.c: New test. * gcc.dg/plugin/diagnostic-test-text-art-none.c: New test. * gcc.dg/plugin/diagnostic-test-text-art-unicode-bw.c: New test. * gcc.dg/plugin/diagnostic-test-text-art-unicode-color.c: New test. * gcc.dg/plugin/diagnostic_plugin_test_text_art.c: New test plugin. * gcc.dg/plugin/plugin.exp (plugin_test_list): Add them. libcpp/ChangeLog: * charset.cc (get_cppchar_property): New function template, based on... (cpp_wcwidth): ...this function. Rework to use the above. Include "combining-chars.inc". (cpp_is_combining_char): New function Include "printable-chars.inc". (cpp_is_printable_char): New function * combining-chars.inc: New file, generated by contrib/unicode/gen-combining-chars.py. * include/cpplib.h (cpp_is_combining_char): New function decl. (cpp_is_printable_char): New function decl. * printable-chars.inc: New file, generated by contrib/unicode/gen-printable-chars.py. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
1772 lines
56 KiB
C++
1772 lines
56 KiB
C++
/* SARIF output for diagnostics
|
|
Copyright (C) 2018-2023 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"
|
|
#include "diagnostic-diagram.h"
|
|
#include "text-art/canvas.h"
|
|
|
|
class sarif_builder;
|
|
|
|
/* Subclass of json::object for SARIF invocation objects
|
|
(SARIF v2.1.0 section 3.20). */
|
|
|
|
class sarif_invocation : public json::object
|
|
{
|
|
public:
|
|
sarif_invocation ()
|
|
: m_notifications_arr (new json::array ()),
|
|
m_success (true)
|
|
{}
|
|
|
|
void add_notification_for_ice (diagnostic_context *context,
|
|
diagnostic_info *diagnostic,
|
|
sarif_builder *builder);
|
|
void prepare_to_flush ();
|
|
|
|
private:
|
|
json::array *m_notifications_arr;
|
|
bool m_success;
|
|
};
|
|
|
|
/* 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);
|
|
void on_diagram (diagnostic_context *context,
|
|
const diagnostic_diagram &diagram,
|
|
sarif_builder *builder);
|
|
|
|
private:
|
|
void add_related_location (json::object *location_obj);
|
|
|
|
json::array *m_related_locations_arr;
|
|
};
|
|
|
|
/* Subclass of json::object for SARIF notification objects
|
|
(SARIF v2.1.0 section 3.58).
|
|
|
|
This subclass is specifically for notifying when an
|
|
internal compiler error occurs. */
|
|
|
|
class sarif_ice_notification : public json::object
|
|
{
|
|
public:
|
|
sarif_ice_notification (diagnostic_context *context,
|
|
diagnostic_info *diagnostic,
|
|
sarif_builder *builder);
|
|
};
|
|
|
|
/* 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 emit_diagram (diagnostic_context *context,
|
|
const diagnostic_diagram &diagram);
|
|
void end_group ();
|
|
|
|
void flush_to_file (FILE *outf);
|
|
|
|
json::array *make_locations_arr (diagnostic_info *diagnostic);
|
|
json::object *make_location_object (const rich_location &rich_loc,
|
|
const logical_location *logical_loc);
|
|
json::object *make_message_object (const char *msg) const;
|
|
json::object *
|
|
make_message_object_for_diagram (diagnostic_context *context,
|
|
const diagnostic_diagram &diagram);
|
|
|
|
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 (sarif_invocation *invocation_obj,
|
|
json::array *results);
|
|
json::object *make_run_object (sarif_invocation *invocation_obj,
|
|
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 object for the invocation object. */
|
|
sarif_invocation *m_invocation_obj;
|
|
|
|
/* 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_invocation : public json::object. */
|
|
|
|
/* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT.
|
|
Add an object representing the ICE to the notifications array. */
|
|
|
|
void
|
|
sarif_invocation::add_notification_for_ice (diagnostic_context *context,
|
|
diagnostic_info *diagnostic,
|
|
sarif_builder *builder)
|
|
{
|
|
m_success = false;
|
|
|
|
sarif_ice_notification *notification_obj
|
|
= new sarif_ice_notification (context, diagnostic, builder);
|
|
m_notifications_arr->append (notification_obj);
|
|
}
|
|
|
|
void
|
|
sarif_invocation::prepare_to_flush ()
|
|
{
|
|
/* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
|
|
set ("executionSuccessful", new json::literal (m_success));
|
|
|
|
/* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
|
|
set ("toolExecutionNotifications", m_notifications_arr);
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
/* 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);
|
|
|
|
add_related_location (location_obj);
|
|
}
|
|
|
|
/* Handle diagrams that occur within a diagnostic group.
|
|
The closest thing in SARIF seems to be to add a location to the
|
|
"releatedLocations" property (SARIF v2.1.0 section 3.27.22),
|
|
and to put the diagram into the "message" property of that location
|
|
(SARIF v2.1.0 section 3.28.5). */
|
|
|
|
void
|
|
sarif_result::on_diagram (diagnostic_context *context,
|
|
const diagnostic_diagram &diagram,
|
|
sarif_builder *builder)
|
|
{
|
|
json::object *location_obj = new json::object ();
|
|
json::object *message_obj
|
|
= builder->make_message_object_for_diagram (context, diagram);
|
|
location_obj->set ("message", message_obj);
|
|
|
|
add_related_location (location_obj);
|
|
}
|
|
|
|
/* Add LOCATION_OBJ to this result's "relatedLocations" array,
|
|
creating it if it doesn't yet exist. */
|
|
|
|
void
|
|
sarif_result::add_related_location (json::object *location_obj)
|
|
{
|
|
if (!m_related_locations_arr)
|
|
{
|
|
m_related_locations_arr = new json::array ();
|
|
set ("relatedLocations", m_related_locations_arr);
|
|
}
|
|
m_related_locations_arr->append (location_obj);
|
|
}
|
|
|
|
/* class sarif_ice_notification : public json::object. */
|
|
|
|
/* sarif_ice_notification's ctor.
|
|
DIAGNOSTIC is an internal compiler error. */
|
|
|
|
sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
|
|
diagnostic_info *diagnostic,
|
|
sarif_builder *builder)
|
|
{
|
|
/* "locations" property (SARIF v2.1.0 section 3.58.4). */
|
|
json::array *locations_arr = builder->make_locations_arr (diagnostic);
|
|
set ("locations", locations_arr);
|
|
|
|
/* "message" property (SARIF v2.1.0 section 3.85.5). */
|
|
json::object *message_obj
|
|
= builder->make_message_object (pp_formatted_text (context->printer));
|
|
pp_clear_output_area (context->printer);
|
|
set ("message", message_obj);
|
|
|
|
/* "level" property (SARIF v2.1.0 section 3.58.6). */
|
|
set ("level", new json::string ("error"));
|
|
}
|
|
|
|
/* class sarif_builder. */
|
|
|
|
/* sarif_builder's ctor. */
|
|
|
|
sarif_builder::sarif_builder (diagnostic_context *context)
|
|
: m_context (context),
|
|
m_invocation_obj (new sarif_invocation ()),
|
|
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 (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT)
|
|
{
|
|
m_invocation_obj->add_notification_for_ice (context, diagnostic, this);
|
|
return;
|
|
}
|
|
|
|
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 diagnostic_context::m_diagrams.m_emission_cb
|
|
for SARIF output. */
|
|
|
|
void
|
|
sarif_builder::emit_diagram (diagnostic_context *context,
|
|
const diagnostic_diagram &diagram)
|
|
{
|
|
/* We must be within the emission of a top-level diagnostic. */
|
|
gcc_assert (m_cur_group_result);
|
|
m_cur_group_result->on_diagram (context, diagram, this);
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
m_invocation_obj->prepare_to_flush ();
|
|
json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
|
|
top->dump (outf);
|
|
m_invocation_obj = NULL;
|
|
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 = make_locations_arr (diagnostic);
|
|
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;
|
|
}
|
|
|
|
/* Make an array suitable for use as the "locations" property of:
|
|
- a "result" object (SARIF v2.1.0 section 3.27.12), or
|
|
- a "notification" object (SARIF v2.1.0 section 3.58.4). */
|
|
|
|
json::array *
|
|
sarif_builder::make_locations_arr (diagnostic_info *diagnostic)
|
|
{
|
|
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);
|
|
return locations_arr;
|
|
}
|
|
|
|
/* 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.get ());
|
|
location_obj->set ("message", message_obj);
|
|
|
|
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 || LOCATION_FILE (loc) == NULL)
|
|
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 message object (SARIF v2.1.0 section 3.11) for DIAGRAM.
|
|
We emit the diagram as a code block within the Markdown part
|
|
of the message. */
|
|
|
|
json::object *
|
|
sarif_builder::make_message_object_for_diagram (diagnostic_context *context,
|
|
const diagnostic_diagram &diagram)
|
|
{
|
|
json::object *message_obj = new json::object ();
|
|
|
|
/* "text" property (SARIF v2.1.0 section 3.11.8). */
|
|
message_obj->set ("text", new json::string (diagram.get_alt_text ()));
|
|
|
|
char *saved_prefix = pp_take_prefix (context->printer);
|
|
pp_set_prefix (context->printer, NULL);
|
|
|
|
/* "To produce a code block in Markdown, simply indent every line of
|
|
the block by at least 4 spaces or 1 tab."
|
|
Here we use 4 spaces. */
|
|
diagram.get_canvas ().print_to_pp (context->printer, " ");
|
|
pp_set_prefix (context->printer, saved_prefix);
|
|
|
|
/* "markdown" property (SARIF v2.1.0 section 3.11.9). */
|
|
message_obj->set ("markdown",
|
|
new json::string (pp_formatted_text (context->printer)));
|
|
|
|
pp_clear_output_area (context->printer);
|
|
|
|
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 INVOCATION_OBJ and RESULTS. */
|
|
|
|
json::object *
|
|
sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
|
|
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 (invocation_obj, 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 INVOCATION_OBJ and RESULTS. */
|
|
|
|
json::object *
|
|
sarif_builder::make_run_object (sarif_invocation *invocation_obj,
|
|
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);
|
|
|
|
/* "invocations" property (SARIF v2.1.0 section 3.14.11). */
|
|
{
|
|
json::array *invocations_arr = new json::array ();
|
|
invocations_arr->append (invocation_obj);
|
|
run_obj->set ("invocations", invocations_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;
|
|
}
|
|
|
|
/* 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
|
|
{
|
|
/* Let input.cc handle any charset conversion. */
|
|
char_span utf8_content = get_source_file_content (filename);
|
|
if (!utf8_content)
|
|
return NULL;
|
|
|
|
/* Don't add it if it's not valid UTF-8. */
|
|
if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
|
|
return NULL;
|
|
|
|
json::object *artifact_content_obj = new json::object ();
|
|
artifact_content_obj->set ("text",
|
|
new json::string (utf8_content.get_buffer (),
|
|
utf8_content.length ()));
|
|
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;
|
|
|
|
/* Don't add it if it's not valid UTF-8. */
|
|
if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
|
|
{
|
|
free (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);
|
|
}
|
|
|
|
/* Callback for diagnostic_context::ice_handler_cb for when an ICE
|
|
occurs. */
|
|
|
|
static void
|
|
sarif_ice_handler (diagnostic_context *context)
|
|
{
|
|
/* Attempt to ensure that a .sarif file is written out. */
|
|
diagnostic_finish (context);
|
|
|
|
/* Print a header for the remaining output to stderr, and
|
|
return, attempting to print the usual ICE messages to
|
|
stderr. Hopefully this will be helpful to the user in
|
|
indicating what's gone wrong (also for DejaGnu, for pruning
|
|
those messages). */
|
|
fnotice (stderr, "Internal compiler error:\n");
|
|
}
|
|
|
|
/* Callback for diagnostic_context::m_diagrams.m_emission_cb. */
|
|
|
|
static void
|
|
sarif_emit_diagram (diagnostic_context *context,
|
|
const diagnostic_diagram &diagram)
|
|
{
|
|
gcc_assert (the_builder);
|
|
the_builder->emit_diagram (context, diagram);
|
|
}
|
|
|
|
/* 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. */
|
|
context->ice_handler_cb = sarif_ice_handler;
|
|
context->m_diagrams.m_emission_cb = sarif_emit_diagram;
|
|
|
|
/* The metadata is handled in SARIF format, rather than as text. */
|
|
context->show_cwe = false;
|
|
context->show_rules = 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);
|
|
}
|