blob: c740c29b2a32ed7b173e3328635fa5d48c3f5fdc [file] [log] [blame]
/* A program for re-emitting diagnostics saved in SARIF form.
Copyright (C) 2022-2025 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"
#define INCLUDE_STRING
#define INCLUDE_VECTOR
#include "system.h"
#include "coretypes.h"
#include "version.h"
#include "intl.h"
#include "libgdiagnostics++.h"
#include "libsarifreplay.h"
static const char *progname;
static void
set_defaults (replay_options &replay_opts)
{
/* Defaults. */
replay_opts.m_echo_file = false;
replay_opts.m_json_comments = false;
replay_opts.m_verbose = false;
replay_opts.m_diagnostics_colorize = DIAGNOSTIC_COLORIZE_IF_TTY;
}
struct options
{
options ()
{
set_defaults (m_replay_opts);
}
replay_options m_replay_opts;
std::vector<const char *> m_sarif_filenames;
std::vector<std::string> m_extra_output_specs;
};
static void
print_version ()
{
printf (_("%s %s%s\n"), progname, pkgversion_string,
version_string);
printf ("Copyright %s 2024-2025 Free Software Foundation, Inc.\n",
_("(C)"));
fputs (_("This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"),
stdout);
}
static const char *const usage_msg = (
"sarif-replay [OPTIONS] FILE+\n"
"\n"
" \"Replay\" results from one or more .sarif files as if they were\n"
" GCC diagnostics\n"
"\n"
"Options:\n"
"\n"
" -fdiagnostics-add-output=SCHEME\n"
" Add an additional output sink when replaying diagnostics, as\n"
" per the gcc option\n"
"\n"
" -fdiagnostics-color={never|always|auto}\n"
" Control colorization of diagnostics. Default: auto.\n"
"\n"
" -fjson-comments\n"
" Support C and C++ style comments in .sarif JSON files\n"
"\n"
" --verbose\n"
" Print notes about each .sarif file before and after replaying it.\n"
"\n"
" --echo-file\n"
" Print the filename and file contents to stderr before replaying it.\n"
"\n"
" -v, --version\n"
" Print version and exit.\n"
"\n"
" --usage\n"
" Print this message and exit.\n"
"\n"
"Options for maintainers:\n"
"\n"
" -fdebug-physical-locations\n"
" Dump debugging information about physical locations to stderr.\n"
"\n");
static void
print_usage ()
{
fprintf (stderr, usage_msg);
}
/* If STR starts with PREFIX, return the rest of STR.
Otherwise return nullptr. */
static const char *
str_starts_with (const char *str, const char *prefix)
{
size_t prefix_len = strlen (prefix);
if (0 == strncmp (str, prefix, prefix_len))
return str + prefix_len;
else
return nullptr;
}
static bool
parse_options (int argc, char **argv,
options &opts,
libgdiagnostics::text_sink control_text_sink)
{
libgdiagnostics::manager options_mgr;
options_mgr.set_tool_name ("sarif-replay");
options_mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_NO/*IF_TTY*/);
for (int i = 1; i < argc; ++i)
{
const char *option = argv[i];
bool handled = false;
if (strcmp (option, "-fjson-comments") == 0)
{
opts.m_replay_opts.m_json_comments = true;
handled = true;
}
else if (strcmp (option, "--verbose") == 0)
{
opts.m_replay_opts.m_verbose = true;
handled = true;
}
else if (strcmp (option, "--usage") == 0)
{
print_usage ();
exit (0);
}
else if (strcmp (option, "--echo-file") == 0)
{
opts.m_replay_opts.m_echo_file = true;
handled = true;
}
#define ADD_OUTPUT_OPTION "-fdiagnostics-add-output="
else if (const char *arg
= str_starts_with (option, ADD_OUTPUT_OPTION))
{
opts.m_extra_output_specs.push_back (std::string (arg));
handled = true;
}
else if (strcmp (option, "-fdiagnostics-color=never") == 0)
{
opts.m_replay_opts.m_diagnostics_colorize = DIAGNOSTIC_COLORIZE_NO;
control_text_sink.set_colorize
(opts.m_replay_opts.m_diagnostics_colorize);
handled = true;
}
else if (strcmp (option, "-fdiagnostics-color=always") == 0)
{
opts.m_replay_opts.m_diagnostics_colorize = DIAGNOSTIC_COLORIZE_YES;
control_text_sink.set_colorize
(opts.m_replay_opts.m_diagnostics_colorize);
handled = true;
}
else if (strcmp (option, "-fdiagnostics-color=auto") == 0)
{
opts.m_replay_opts.m_diagnostics_colorize
= DIAGNOSTIC_COLORIZE_IF_TTY;
control_text_sink.set_colorize
(opts.m_replay_opts.m_diagnostics_colorize);
handled = true;
}
else if (strcmp (option, "-v") == 0
|| strcmp (option, "--version") == 0)
{
print_version ();
exit (0);
}
else if (strcmp (option, "-fdebug-physical-locations") == 0)
{
opts.m_replay_opts.m_debug_physical_locations = true;
handled = true;
}
if (!handled)
{
if (option[0] == '-')
{
auto err = options_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR);
err.finish ("unrecognized option: %qs", option);
return false;
}
else
opts.m_sarif_filenames.push_back (option);
}
}
if (opts.m_sarif_filenames.size () == 0)
{
auto err = options_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR);
err.finish ("need at least one .sarif file to dump");
return false;
}
return true;
}
static const char *
get_progname (const char *argv0)
{
const char *p = argv0 + strlen (argv0);
while (p != argv0 && !IS_DIR_SEPARATOR (p[-1]))
--p;
return p;
}
/* Entrypoint to sarif-replay command-line tool. */
int
main (int argc, char **argv)
{
progname = get_progname (argv[0]);
xmalloc_set_program_name (progname);
libgdiagnostics::manager control_mgr;
control_mgr.set_tool_name (progname);
libgdiagnostics::text_sink control_text_sink
= control_mgr.add_text_sink (stderr, DIAGNOSTIC_COLORIZE_IF_TTY);
options opts;
if (!parse_options (argc, argv, opts, control_text_sink))
{
print_usage ();
return -1;
}
int failures = 0;
for (auto filename : opts.m_sarif_filenames)
{
if (opts.m_replay_opts.m_verbose)
{
auto note = control_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_NOTE);
note.finish ("about to replay %qs...", filename);
}
libgdiagnostics::manager playback_mgr;
playback_mgr.set_debug_physical_locations
(opts.m_replay_opts.m_debug_physical_locations);
playback_mgr.add_text_sink (stderr,
opts.m_replay_opts.m_diagnostics_colorize);
for (auto spec : opts.m_extra_output_specs)
if (playback_mgr.add_sink_from_spec
(ADD_OUTPUT_OPTION,
spec.c_str (),
libgdiagnostics::manager (control_mgr.m_inner, false)))
return -1;
int result = sarif_replay_path (filename,
playback_mgr.m_inner,
control_mgr.m_inner,
&opts.m_replay_opts);
if (result)
++failures;
if (opts.m_replay_opts.m_verbose)
{
auto note = control_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_NOTE);
note.finish ("...finished replaying %qs", filename);
}
}
return failures;
}