mirror of git://gcc.gnu.org/git/gcc.git
libgdiagnostics: sarif-replay: add extra sinks via -fdiagnostics-add-output= [PR116792,PR116163]
This patch refactors the support for -fdiagnostics-add-output=SCHEME
from GCC's options parsing so that it is also available to
sarif-replay and to other clients of libgdiagnostics.
With this users of sarif-replay and other such tools can generate HTML
or SARIF as well as text output, using the same
-fdiagnostics-add-output=SCHEME
as GCC.
As a test, the patch adds support for this option to the dg-lint
script below "contrib". For example dg-lint can now generate text,
html, and sarif output via:
LD_LIBRARY_PATH=../build/gcc/ \
./contrib/dg-lint/dg-lint \
contrib/dg-lint/test-*.c \
-fdiagnostics-add-output=experimental-html:file=dg-lint-tests.html \
-fdiagnostics-add-output=sarif:file=dg-lint-tests.sarif
where the HTML output from dg-lint can be seen here:
https://dmalcolm.fedorapeople.org/gcc/2025-06-20/dg-lint-tests.html
the sarif output here:
https://dmalcolm.fedorapeople.org/gcc/2025-06-23/dg-lint-tests.sarif
and a screenshot of VS Code viewing the sarif output is here:
https://dmalcolm.fedorapeople.org/gcc/2025-06-23/vscode-viewing-dg-lint-sarif-output.png
As well as allowing sarif-replay to generate HTML, this patch allows
sarif-replay to also generate SARIF. Ideally this would faithfully
round-trip all the data, but it's not perfect (which I'm tracking as
PR sarif-replay/120792).
contrib/ChangeLog:
PR other/116792
PR testsuite/116163
PR sarif-replay/120792
* dg-lint/dg-lint: Add -fdiagnostics-add-output.
* dg-lint/libgdiagnostics.py: Add
diagnostic_manager_add_sink_from_spec.
(Manager.add_sink_from_spec): New.
gcc/ChangeLog:
PR other/116792
PR testsuite/116163
PR sarif-replay/120792
* Makefile.in (OBJS-libcommon): Add diagnostic-output-spec.o.
* diagnostic-format-html.cc (html_builder::html_builder): Ensure
title is non-empty.
* diagnostic-output-spec.cc: New file, taken from material in
opts-diagnostic.cc.
* diagnostic-output-spec.h: New file.
* diagnostic.cc (diagnostic_context::set_main_input_filename):
New.
* diagnostic.h (diagnostic_context::set_main_input_filename): New
decl.
* doc/libgdiagnostics/topics/compatibility.rst
(LIBGDIAGNOSTICS_ABI_2): New.
* doc/libgdiagnostics/topics/diagnostic-manager.rst
(diagnostic_manager_add_sink_from_spec): New.
(diagnostic_manager_set_analysis_target): New.
* libgdiagnostics++.h (manager::add_sink_from_spec): New.
(manager::set_analysis_target): New.
* libgdiagnostics.cc: Include "diagnostic-output-spec.h".
(struct spec_context): New.
(diagnostic_manager_add_sink_from_spec): New.
(diagnostic_manager_set_analysis_target): New.
* libgdiagnostics.h
(LIBDIAGNOSTICS_HAVE_diagnostic_manager_add_sink_from_spec): New
define.
(diagnostic_manager_add_sink_from_spec): New decl.
(LIBDIAGNOSTICS_HAVE_diagnostic_manager_set_analysis_target): New
define.
(diagnostic_manager_set_analysis_target): New decl.
* libgdiagnostics.map (LIBGDIAGNOSTICS_ABI_2): New.
* libsarifreplay.cc (sarif_replayer::handle_artifact_obj): Looks
for "analysisTarget" in roles and call set_analysis_target using
the artifact if found.
* opts-diagnostic.cc: Refactor, moving material to
diagnostic-output-spec.cc.
(struct opt_spec_context): New.
(handle_OPT_fdiagnostics_add_output_): Use opt_spec_context.
(handle_OPT_fdiagnostics_set_output_): Likewise.
* sarif-replay.cc: Define INCLUDE_STRING.
(struct options): Add m_extra_output_specs.
(usage_msg): Add -fdiagnostics-add-output=SCHEME.
(str_starts_with): New.
(parse_options): Add -fdiagnostics-add-output=SCHEME.
(main): Likewise.
* selftest-run-tests.cc (selftest::run_tests): Call
diagnostic_output_spec_cc_tests rather than
opts_diagnostic_cc_tests.
* selftest.h (selftest::diagnostic_output_spec_cc_tests):
Replace...
(selftest::opts_diagnostic_cc_tests): ...this.
gcc/testsuite/ChangeLog:
PR other/116792
PR testsuite/116163
PR sarif-replay/120792
* sarif-replay.dg/2.1.0-valid/signal-1-check-html.py: New test
script.
* sarif-replay.dg/2.1.0-valid/signal-1.c.sarif: Add html and sarif
generation to options. Invoke the new script to verify that HTML
and SARIF is generated.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
parent
e6406aefd1
commit
d0142e1474
|
|
@ -380,9 +380,17 @@ def skip_file(filename):
|
|||
def main(argv):
|
||||
parser = argparse.ArgumentParser()#usage=__doc__)
|
||||
parser.add_argument('paths', nargs='+', type=pathlib.Path)
|
||||
parser.add_argument('-fdiagnostics-add-output', action='append')
|
||||
opts = parser.parse_args(argv[1:])
|
||||
|
||||
ctxt = Context()
|
||||
control_mgr = libgdiagnostics.Manager()
|
||||
control_mgr.add_text_sink()
|
||||
for scheme in opts.fdiagnostics_add_output:
|
||||
ctxt.mgr.add_sink_from_spec("-fdiagnostics-add-output=",
|
||||
scheme,
|
||||
control_mgr)
|
||||
|
||||
for path in opts.paths:
|
||||
if path.is_dir():
|
||||
for dirpath, dirnames, filenames in os.walk(path):
|
||||
|
|
|
|||
|
|
@ -124,6 +124,13 @@ cdll.diagnostic_add_fix_it_hint_replace.argtypes \
|
|||
ctypes.c_char_p]
|
||||
cdll.diagnostic_add_fix_it_hint_replace.restype = None
|
||||
|
||||
cdll.diagnostic_manager_add_sink_from_spec.argtypes \
|
||||
= [c_diagnostic_manager_ptr,
|
||||
ctypes.c_char_p,
|
||||
ctypes.c_char_p,
|
||||
c_diagnostic_manager_ptr]
|
||||
cdll.diagnostic_manager_add_sink_from_spec.restype = ctypes.c_int
|
||||
|
||||
# Helper functions
|
||||
|
||||
def _to_utf8(s: str):
|
||||
|
|
@ -156,6 +163,16 @@ class Manager:
|
|||
c_stderr,
|
||||
DIAGNOSTIC_COLORIZE_IF_TTY)
|
||||
|
||||
def add_sink_from_spec(self, option_name: str, scheme: str, control_mgr):
|
||||
assert self.c_mgr
|
||||
assert control_mgr.c_mgr
|
||||
res = cdll.diagnostic_manager_add_sink_from_spec (self.c_mgr,
|
||||
_to_utf8(option_name),
|
||||
_to_utf8(scheme),
|
||||
control_mgr.c_mgr)
|
||||
if res:
|
||||
raise RuntimeError()
|
||||
|
||||
def get_file(self, path: str, sarif_lang: str = None):
|
||||
assert self.c_mgr
|
||||
assert path
|
||||
|
|
|
|||
|
|
@ -1858,6 +1858,7 @@ OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \
|
|||
diagnostic-format-text.o \
|
||||
diagnostic-global-context.o \
|
||||
diagnostic-macro-unwinding.o \
|
||||
diagnostic-output-spec.o \
|
||||
diagnostic-path.o \
|
||||
diagnostic-path-output.o \
|
||||
diagnostic-show-locus.o \
|
||||
|
|
|
|||
|
|
@ -426,6 +426,7 @@ html_builder::html_builder (diagnostic_context &context,
|
|||
{
|
||||
xml::auto_print_element title (xp, "title", true);
|
||||
m_title_element = xp.get_insertion_point ();
|
||||
m_title_element->add_text (" ");
|
||||
}
|
||||
|
||||
if (m_html_gen_opts.m_css)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,828 @@
|
|||
/* Support for the DSL of -fdiagnostics-add-output= and
|
||||
-fdiagnostics-set-output=.
|
||||
Copyright (C) 2024-2025 Free Software Foundation, Inc.
|
||||
|
||||
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/>. */
|
||||
|
||||
/* This file implements the domain-specific language for the options
|
||||
-fdiagnostics-add-output= and -fdiagnostics-set-output=, and for
|
||||
the "diagnostic_manager_add_sink_from_spec" entrypoint to
|
||||
libgdiagnostics. */
|
||||
|
||||
#include "config.h"
|
||||
#define INCLUDE_ARRAY
|
||||
#define INCLUDE_STRING
|
||||
#define INCLUDE_VECTOR
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "version.h"
|
||||
#include "intl.h"
|
||||
#include "diagnostic.h"
|
||||
#include "diagnostic-color.h"
|
||||
#include "diagnostic-format.h"
|
||||
#include "diagnostic-format-html.h"
|
||||
#include "diagnostic-format-text.h"
|
||||
#include "diagnostic-format-sarif.h"
|
||||
#include "selftest.h"
|
||||
#include "selftest-diagnostic.h"
|
||||
#include "pretty-print-markup.h"
|
||||
#include "diagnostic-output-spec.h"
|
||||
|
||||
/* A namespace for handling the DSL of the arguments of
|
||||
-fdiagnostics-add-output= and -fdiagnostics-set-output=. */
|
||||
|
||||
namespace diagnostics_output_spec {
|
||||
|
||||
/* Decls. */
|
||||
|
||||
struct scheme_name_and_params
|
||||
{
|
||||
std::string m_scheme_name;
|
||||
std::vector<std::pair<std::string, std::string>> m_kvs;
|
||||
};
|
||||
|
||||
/* Class for parsing the arguments of -fdiagnostics-add-output= and
|
||||
-fdiagnostics-set-output=, and making diagnostic_output_format
|
||||
instances (or issuing errors). */
|
||||
|
||||
class output_factory
|
||||
{
|
||||
public:
|
||||
class scheme_handler
|
||||
{
|
||||
public:
|
||||
scheme_handler (std::string scheme_name)
|
||||
: m_scheme_name (std::move (scheme_name))
|
||||
{}
|
||||
virtual ~scheme_handler () {}
|
||||
|
||||
const std::string &get_scheme_name () const { return m_scheme_name; }
|
||||
|
||||
virtual std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
diagnostic_context &dc,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const = 0;
|
||||
|
||||
protected:
|
||||
bool
|
||||
parse_bool_value (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &value,
|
||||
bool &out) const
|
||||
{
|
||||
if (value == "yes")
|
||||
{
|
||||
out = true;
|
||||
return true;
|
||||
}
|
||||
else if (value == "no")
|
||||
{
|
||||
out = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctxt.report_error
|
||||
("%<%s%s%>:"
|
||||
" unexpected value %qs for key %qs; expected %qs or %qs",
|
||||
ctxt.get_option_name (), unparsed_arg,
|
||||
value.c_str (),
|
||||
key.c_str (),
|
||||
"yes", "no");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
template <typename EnumType, size_t NumValues>
|
||||
bool
|
||||
parse_enum_value (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &value,
|
||||
const std::array<std::pair<const char *, EnumType>, NumValues> &value_names,
|
||||
EnumType &out) const
|
||||
{
|
||||
for (auto &iter : value_names)
|
||||
if (value == iter.first)
|
||||
{
|
||||
out = iter.second;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto_vec<const char *> known_values;
|
||||
for (auto iter : value_names)
|
||||
known_values.safe_push (iter.first);
|
||||
pp_markup::comma_separated_quoted_strings e (known_values);
|
||||
ctxt.report_error
|
||||
("%<%s%s%>:"
|
||||
" unexpected value %qs for key %qs; known values: %e",
|
||||
ctxt.get_option_name (), unparsed_arg,
|
||||
value.c_str (),
|
||||
key.c_str (),
|
||||
&e);
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string m_scheme_name;
|
||||
};
|
||||
|
||||
output_factory ();
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
diagnostic_context &dc,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg);
|
||||
|
||||
const scheme_handler *get_scheme_handler (const std::string &scheme_name);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<scheme_handler>> m_scheme_handlers;
|
||||
};
|
||||
|
||||
class text_scheme_handler : public output_factory::scheme_handler
|
||||
{
|
||||
public:
|
||||
text_scheme_handler () : scheme_handler ("text") {}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
diagnostic_context &dc,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const final override;
|
||||
};
|
||||
|
||||
class sarif_scheme_handler : public output_factory::scheme_handler
|
||||
{
|
||||
public:
|
||||
sarif_scheme_handler () : scheme_handler ("sarif") {}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
diagnostic_context &dc,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const final override;
|
||||
};
|
||||
|
||||
class html_scheme_handler : public output_factory::scheme_handler
|
||||
{
|
||||
public:
|
||||
html_scheme_handler () : scheme_handler ("experimental-html") {}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
diagnostic_context &dc,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const final override;
|
||||
};
|
||||
|
||||
/* struct context. */
|
||||
|
||||
void
|
||||
context::report_error (const char *gmsgid, ...) const
|
||||
{
|
||||
va_list ap;
|
||||
va_start (ap, gmsgid);
|
||||
report_error_va (gmsgid, &ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
void
|
||||
context::report_unknown_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &scheme_name,
|
||||
auto_vec<const char *> &known_keys) const
|
||||
{
|
||||
pp_markup::comma_separated_quoted_strings e (known_keys);
|
||||
report_error
|
||||
("%<%s%s%>:"
|
||||
" unknown key %qs for format %qs; known keys: %e",
|
||||
get_option_name (), unparsed_arg,
|
||||
key.c_str (), scheme_name.c_str (), &e);
|
||||
}
|
||||
|
||||
void
|
||||
context::report_missing_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &scheme_name,
|
||||
const char *metavar) const
|
||||
{
|
||||
report_error
|
||||
("%<%s%s%>:"
|
||||
" missing required key %qs for format %qs;"
|
||||
" try %<%s%s:%s=%s%>",
|
||||
get_option_name (), unparsed_arg,
|
||||
key.c_str (), scheme_name.c_str (),
|
||||
get_option_name (), scheme_name.c_str (), key.c_str (), metavar);
|
||||
}
|
||||
|
||||
diagnostic_output_file
|
||||
context::open_output_file (label_text &&filename) const
|
||||
{
|
||||
FILE *outf = fopen (filename.get (), "w");
|
||||
if (!outf)
|
||||
{
|
||||
report_error ("unable to open %qs: %m", filename.get ());
|
||||
return diagnostic_output_file (nullptr, false, std::move (filename));
|
||||
}
|
||||
return diagnostic_output_file (outf, true, std::move (filename));
|
||||
}
|
||||
|
||||
static std::unique_ptr<scheme_name_and_params>
|
||||
parse (const context &ctxt, const char *unparsed_arg)
|
||||
{
|
||||
scheme_name_and_params result;
|
||||
if (const char *const colon = strchr (unparsed_arg, ':'))
|
||||
{
|
||||
result.m_scheme_name = std::string (unparsed_arg, colon - unparsed_arg);
|
||||
/* Expect zero of more of KEY=VALUE,KEY=VALUE, etc .*/
|
||||
const char *iter = colon + 1;
|
||||
const char *last_separator = ":";
|
||||
while (iter)
|
||||
{
|
||||
/* Look for a non-empty key string followed by '='. */
|
||||
const char *eq = strchr (iter, '=');
|
||||
if (eq == nullptr || eq == iter)
|
||||
{
|
||||
/* Missing '='. */
|
||||
ctxt.report_error
|
||||
("%<%s%s%>:"
|
||||
" expected KEY=VALUE-style parameter for format %qs"
|
||||
" after %qs;"
|
||||
" got %qs",
|
||||
ctxt.get_option_name (), unparsed_arg,
|
||||
result.m_scheme_name.c_str (),
|
||||
last_separator,
|
||||
iter);
|
||||
return nullptr;
|
||||
}
|
||||
std::string key = std::string (iter, eq - iter);
|
||||
std::string value;
|
||||
const char *comma = strchr (iter, ',');
|
||||
if (comma)
|
||||
{
|
||||
value = std::string (eq + 1, comma - (eq + 1));
|
||||
iter = comma + 1;
|
||||
last_separator = ",";
|
||||
}
|
||||
else
|
||||
{
|
||||
value = std::string (eq + 1);
|
||||
iter = nullptr;
|
||||
}
|
||||
result.m_kvs.push_back ({std::move (key), std::move (value)});
|
||||
}
|
||||
}
|
||||
else
|
||||
result.m_scheme_name = unparsed_arg;
|
||||
return std::make_unique<scheme_name_and_params> (std::move (result));
|
||||
}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
context::parse_and_make_sink (const char *unparsed_arg,
|
||||
diagnostic_context &dc)
|
||||
{
|
||||
auto parsed_arg = diagnostics_output_spec::parse (*this, unparsed_arg);
|
||||
if (!parsed_arg)
|
||||
return nullptr;
|
||||
|
||||
diagnostics_output_spec::output_factory factory;
|
||||
return factory.make_sink (*this, dc, unparsed_arg, *parsed_arg);
|
||||
}
|
||||
|
||||
/* class output_factory::scheme_handler. */
|
||||
|
||||
/* class output_factory. */
|
||||
|
||||
output_factory::output_factory ()
|
||||
{
|
||||
m_scheme_handlers.push_back (std::make_unique<text_scheme_handler> ());
|
||||
m_scheme_handlers.push_back (std::make_unique<sarif_scheme_handler> ());
|
||||
m_scheme_handlers.push_back (std::make_unique<html_scheme_handler> ());
|
||||
}
|
||||
|
||||
const output_factory::scheme_handler *
|
||||
output_factory::get_scheme_handler (const std::string &scheme_name)
|
||||
{
|
||||
for (auto &iter : m_scheme_handlers)
|
||||
if (iter->get_scheme_name () == scheme_name)
|
||||
return iter.get ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
output_factory::make_sink (const context &ctxt,
|
||||
diagnostic_context &dc,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg)
|
||||
{
|
||||
auto scheme_handler = get_scheme_handler (parsed_arg.m_scheme_name);
|
||||
if (!scheme_handler)
|
||||
{
|
||||
auto_vec<const char *> strings;
|
||||
for (auto &iter : m_scheme_handlers)
|
||||
strings.safe_push (iter->get_scheme_name ().c_str ());
|
||||
pp_markup::comma_separated_quoted_strings e (strings);
|
||||
ctxt.report_error ("%<%s%s%>:"
|
||||
" unrecognized format %qs; known formats: %e",
|
||||
ctxt.get_option_name (), unparsed_arg,
|
||||
parsed_arg.m_scheme_name.c_str (), &e);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return scheme_handler->make_sink (ctxt, dc, unparsed_arg, parsed_arg);
|
||||
}
|
||||
|
||||
/* class text_scheme_handler : public output_factory::scheme_handler. */
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
text_scheme_handler::make_sink (const context &ctxt,
|
||||
diagnostic_context &dc,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const
|
||||
{
|
||||
bool show_color = pp_show_color (dc.get_reference_printer ());
|
||||
bool show_nesting = false;
|
||||
bool show_locations_in_nesting = true;
|
||||
bool show_levels = false;
|
||||
for (auto& iter : parsed_arg.m_kvs)
|
||||
{
|
||||
const std::string &key = iter.first;
|
||||
const std::string &value = iter.second;
|
||||
if (key == "color")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_color))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "experimental-nesting")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_nesting))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "experimental-nesting-show-locations")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_locations_in_nesting))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "experimental-nesting-show-levels")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_levels))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Key not found. */
|
||||
auto_vec<const char *> known_keys;
|
||||
known_keys.safe_push ("color");
|
||||
known_keys.safe_push ("experimental-nesting");
|
||||
known_keys.safe_push ("experimental-nesting-show-locations");
|
||||
known_keys.safe_push ("experimental-nesting-show-levels");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
|
||||
known_keys);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto sink = std::make_unique<diagnostic_text_output_format> (dc);
|
||||
sink->set_show_nesting (show_nesting);
|
||||
sink->set_show_locations_in_nesting (show_locations_in_nesting);
|
||||
sink->set_show_nesting_levels (show_levels);
|
||||
return sink;
|
||||
}
|
||||
|
||||
/* class sarif_scheme_handler : public output_factory::scheme_handler. */
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
sarif_scheme_handler::make_sink (const context &ctxt,
|
||||
diagnostic_context &dc,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const
|
||||
{
|
||||
label_text filename;
|
||||
enum sarif_serialization_kind serialization_kind
|
||||
= sarif_serialization_kind::json;
|
||||
enum sarif_version version = sarif_version::v2_1_0;
|
||||
bool xml_state = false;
|
||||
for (auto& iter : parsed_arg.m_kvs)
|
||||
{
|
||||
const std::string &key = iter.first;
|
||||
const std::string &value = iter.second;
|
||||
if (key == "file")
|
||||
{
|
||||
filename = label_text::take (xstrdup (value.c_str ()));
|
||||
continue;
|
||||
}
|
||||
if (key == "serialization")
|
||||
{
|
||||
static const std::array<std::pair<const char *, enum sarif_serialization_kind>,
|
||||
(size_t)sarif_serialization_kind::num_values> value_names
|
||||
{{{"json", sarif_serialization_kind::json}}};
|
||||
|
||||
if (!parse_enum_value<enum sarif_serialization_kind>
|
||||
(ctxt, unparsed_arg,
|
||||
key, value,
|
||||
value_names,
|
||||
serialization_kind))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "version")
|
||||
{
|
||||
static const std::array<std::pair<const char *, enum sarif_version>,
|
||||
(size_t)sarif_version::num_versions> value_names
|
||||
{{{"2.1", sarif_version::v2_1_0},
|
||||
{"2.2-prerelease", sarif_version::v2_2_prerelease_2024_08_08}}};
|
||||
|
||||
if (!parse_enum_value<enum sarif_version> (ctxt, unparsed_arg,
|
||||
key, value,
|
||||
value_names,
|
||||
version))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "xml-state")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
xml_state))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Key not found. */
|
||||
auto_vec<const char *> known_keys;
|
||||
known_keys.safe_push ("file");
|
||||
known_keys.safe_push ("serialization");
|
||||
known_keys.safe_push ("version");
|
||||
known_keys.safe_push ("xml-state");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
|
||||
known_keys);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
diagnostic_output_file output_file;
|
||||
if (filename.get ())
|
||||
output_file = ctxt.open_output_file (std::move (filename));
|
||||
else
|
||||
// Default filename
|
||||
{
|
||||
const char *basename = ctxt.get_base_filename ();
|
||||
if (!basename)
|
||||
{
|
||||
ctxt.report_missing_key (unparsed_arg,
|
||||
"file",
|
||||
get_scheme_name (),
|
||||
"FILENAME");
|
||||
return nullptr;
|
||||
}
|
||||
output_file
|
||||
= diagnostic_output_format_open_sarif_file
|
||||
(dc,
|
||||
ctxt.get_affected_location_mgr (),
|
||||
basename,
|
||||
serialization_kind);
|
||||
}
|
||||
if (!output_file)
|
||||
return nullptr;
|
||||
|
||||
sarif_generation_options sarif_gen_opts;
|
||||
sarif_gen_opts.m_version = version;
|
||||
sarif_gen_opts.m_xml_state = xml_state;
|
||||
|
||||
std::unique_ptr<sarif_serialization_format> serialization_obj;
|
||||
switch (serialization_kind)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
case sarif_serialization_kind::json:
|
||||
serialization_obj
|
||||
= std::make_unique<sarif_serialization_format_json> (true);
|
||||
break;
|
||||
}
|
||||
|
||||
auto sink = make_sarif_sink (dc,
|
||||
*ctxt.get_affected_location_mgr (),
|
||||
std::move (serialization_obj),
|
||||
sarif_gen_opts,
|
||||
std::move (output_file));
|
||||
return sink;
|
||||
}
|
||||
|
||||
/* class html_scheme_handler : public output_factory::scheme_handler. */
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
html_scheme_handler::make_sink (const context &ctxt,
|
||||
diagnostic_context &dc,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const
|
||||
{
|
||||
bool css = true;
|
||||
label_text filename;
|
||||
bool javascript = true;
|
||||
bool show_state_diagrams = false;
|
||||
bool show_state_diagram_xml = false;
|
||||
bool show_state_diagram_dot_src = false;
|
||||
for (auto& iter : parsed_arg.m_kvs)
|
||||
{
|
||||
const std::string &key = iter.first;
|
||||
const std::string &value = iter.second;
|
||||
if (key == "css")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
css))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "file")
|
||||
{
|
||||
filename = label_text::take (xstrdup (value.c_str ()));
|
||||
continue;
|
||||
}
|
||||
if (key == "javascript")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
javascript))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "show-state-diagrams")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_state_diagrams))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "show-state-diagram-dot-src")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_state_diagram_dot_src))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "show-state-diagram-xml")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_state_diagram_xml))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Key not found. */
|
||||
auto_vec<const char *> known_keys;
|
||||
known_keys.safe_push ("css");
|
||||
known_keys.safe_push ("file");
|
||||
known_keys.safe_push ("javascript");
|
||||
known_keys.safe_push ("show-state-diagrams");
|
||||
known_keys.safe_push ("show-state-diagram-dot-src");
|
||||
known_keys.safe_push ("show-state-diagram-xml");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
|
||||
known_keys);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
diagnostic_output_file output_file;
|
||||
if (filename.get ())
|
||||
output_file = ctxt.open_output_file (std::move (filename));
|
||||
else
|
||||
// Default filename
|
||||
{
|
||||
const char *basename = ctxt.get_base_filename ();
|
||||
if (!basename)
|
||||
{
|
||||
ctxt.report_missing_key (unparsed_arg,
|
||||
"file",
|
||||
get_scheme_name (),
|
||||
"FILENAME");
|
||||
return nullptr;
|
||||
}
|
||||
output_file
|
||||
= diagnostic_output_format_open_html_file
|
||||
(dc,
|
||||
ctxt.get_affected_location_mgr (),
|
||||
basename);
|
||||
}
|
||||
if (!output_file)
|
||||
return nullptr;
|
||||
|
||||
html_generation_options html_gen_opts;
|
||||
html_gen_opts.m_css = css;
|
||||
html_gen_opts.m_javascript = javascript;
|
||||
html_gen_opts.m_show_state_diagrams = show_state_diagrams;
|
||||
html_gen_opts.m_show_state_diagram_xml = show_state_diagram_xml;
|
||||
html_gen_opts.m_show_state_diagram_dot_src = show_state_diagram_dot_src;
|
||||
|
||||
auto sink = make_html_sink (dc,
|
||||
*ctxt.get_affected_location_mgr (),
|
||||
html_gen_opts,
|
||||
std::move (output_file));
|
||||
return sink;
|
||||
}
|
||||
|
||||
} // namespace diagnostics_output_spec
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
/* RAII class to temporarily override "progname" to the
|
||||
string "PROGNAME". */
|
||||
|
||||
class auto_fix_progname
|
||||
{
|
||||
public:
|
||||
auto_fix_progname ()
|
||||
{
|
||||
m_old_progname = progname;
|
||||
progname = "PROGNAME";
|
||||
}
|
||||
|
||||
~auto_fix_progname ()
|
||||
{
|
||||
progname = m_old_progname;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *m_old_progname;
|
||||
};
|
||||
|
||||
struct parser_test
|
||||
{
|
||||
class test_spec_context : public diagnostics_output_spec::gcc_spec_context
|
||||
{
|
||||
public:
|
||||
test_spec_context (diagnostic_context &dc,
|
||||
line_maps *location_mgr,
|
||||
location_t loc,
|
||||
const char *option_name)
|
||||
: gcc_spec_context (dc,
|
||||
location_mgr,
|
||||
location_mgr,
|
||||
loc,
|
||||
option_name)
|
||||
{
|
||||
}
|
||||
|
||||
const char *
|
||||
get_base_filename () const final override
|
||||
{
|
||||
return "BASE_FILENAME";
|
||||
}
|
||||
};
|
||||
|
||||
parser_test ()
|
||||
: m_dc (),
|
||||
m_ctxt (m_dc, line_table, UNKNOWN_LOCATION, "-fOPTION="),
|
||||
m_fmt (m_dc.get_output_format (0))
|
||||
{
|
||||
pp_buffer (m_fmt.get_printer ())->m_flush_p = false;
|
||||
}
|
||||
|
||||
std::unique_ptr<diagnostics_output_spec::scheme_name_and_params>
|
||||
parse (const char *unparsed_arg)
|
||||
{
|
||||
return diagnostics_output_spec::parse (m_ctxt, unparsed_arg);
|
||||
}
|
||||
|
||||
bool execution_failed_p () const
|
||||
{
|
||||
return m_dc.execution_failed_p ();
|
||||
}
|
||||
|
||||
const char *
|
||||
get_diagnostic_text () const
|
||||
{
|
||||
return pp_formatted_text (m_fmt.get_printer ());
|
||||
}
|
||||
|
||||
private:
|
||||
test_diagnostic_context m_dc;
|
||||
test_spec_context m_ctxt;
|
||||
diagnostic_output_format &m_fmt;
|
||||
};
|
||||
|
||||
/* Selftests. */
|
||||
|
||||
static void
|
||||
test_output_arg_parsing ()
|
||||
{
|
||||
auto_fix_quotes fix_quotes;
|
||||
auto_fix_progname fix_progname;
|
||||
|
||||
/* Minimal correct example. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo");
|
||||
ASSERT_EQ (result->m_scheme_name, "foo");
|
||||
ASSERT_EQ (result->m_kvs.size (), 0);
|
||||
ASSERT_FALSE (pt.execution_failed_p ());
|
||||
}
|
||||
|
||||
/* Stray trailing colon with no key/value pairs. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `:';"
|
||||
" got `'\n");
|
||||
}
|
||||
|
||||
/* No key before '='. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:=");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:=':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `:';"
|
||||
" got `='\n");
|
||||
}
|
||||
|
||||
/* No value for key. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:key,");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:key,':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `:';"
|
||||
" got `key,'\n");
|
||||
}
|
||||
|
||||
/* Correct example, with one key/value pair. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:key=value");
|
||||
ASSERT_EQ (result->m_scheme_name, "foo");
|
||||
ASSERT_EQ (result->m_kvs.size (), 1);
|
||||
ASSERT_EQ (result->m_kvs[0].first, "key");
|
||||
ASSERT_EQ (result->m_kvs[0].second, "value");
|
||||
ASSERT_FALSE (pt.execution_failed_p ());
|
||||
}
|
||||
|
||||
/* Stray trailing comma. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:key=value,");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:key=value,':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `,';"
|
||||
" got `'\n");
|
||||
}
|
||||
|
||||
/* Correct example, with two key/value pairs. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:color=red,shape=circle");
|
||||
ASSERT_EQ (result->m_scheme_name, "foo");
|
||||
ASSERT_EQ (result->m_kvs.size (), 2);
|
||||
ASSERT_EQ (result->m_kvs[0].first, "color");
|
||||
ASSERT_EQ (result->m_kvs[0].second, "red");
|
||||
ASSERT_EQ (result->m_kvs[1].first, "shape");
|
||||
ASSERT_EQ (result->m_kvs[1].second, "circle");
|
||||
ASSERT_FALSE (pt.execution_failed_p ());
|
||||
}
|
||||
}
|
||||
|
||||
/* Run all of the selftests within this file. */
|
||||
|
||||
void
|
||||
diagnostic_output_spec_cc_tests ()
|
||||
{
|
||||
test_output_arg_parsing ();
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
|
||||
#endif /* #if CHECKING_P */
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/* Support for the DSL of -fdiagnostics-add-output= and
|
||||
-fdiagnostics-set-output=.
|
||||
Copyright (C) 2024-2025 Free Software Foundation, Inc.
|
||||
|
||||
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/>. */
|
||||
|
||||
#ifndef GCC_DIAGNOSTIC_OUTPUT_SPEC_H
|
||||
#define GCC_DIAGNOSTIC_OUTPUT_SPEC_H
|
||||
|
||||
#include "diagnostic-format.h"
|
||||
#include "diagnostic-output-file.h"
|
||||
|
||||
namespace diagnostics_output_spec {
|
||||
|
||||
/* An abstract base class for handling the DSL of -fdiagnostics-add-output=
|
||||
and -fdiagnostics-set-output=. */
|
||||
|
||||
class context
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
parse_and_make_sink (const char *,
|
||||
diagnostic_context &dc);
|
||||
|
||||
void
|
||||
report_error (const char *gmsgid, ...) const
|
||||
ATTRIBUTE_GCC_DIAG(2,3);
|
||||
|
||||
void
|
||||
report_unknown_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &scheme_name,
|
||||
auto_vec<const char *> &known_keys) const;
|
||||
|
||||
void
|
||||
report_missing_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &scheme_name,
|
||||
const char *metavar) const;
|
||||
|
||||
diagnostic_output_file
|
||||
open_output_file (label_text &&filename) const;
|
||||
|
||||
const char *
|
||||
get_option_name () const { return m_option_name; }
|
||||
|
||||
line_maps *
|
||||
get_affected_location_mgr () const { return m_affected_location_mgr; }
|
||||
|
||||
virtual ~context () {}
|
||||
|
||||
virtual void
|
||||
report_error_va (const char *gmsgid, va_list *ap) const = 0;
|
||||
|
||||
virtual const char *
|
||||
get_base_filename () const = 0;
|
||||
|
||||
protected:
|
||||
context (const char *option_name,
|
||||
line_maps *affected_location_mgr)
|
||||
: m_option_name (option_name),
|
||||
m_affected_location_mgr (affected_location_mgr)
|
||||
{
|
||||
}
|
||||
|
||||
const char *m_option_name;
|
||||
line_maps *m_affected_location_mgr;
|
||||
};
|
||||
|
||||
/* A subclass that implements reporting errors via a diagnostic_context. */
|
||||
|
||||
struct gcc_spec_context : public diagnostics_output_spec::context
|
||||
{
|
||||
public:
|
||||
gcc_spec_context (diagnostic_context &dc,
|
||||
line_maps *affected_location_mgr,
|
||||
line_maps *control_location_mgr,
|
||||
location_t loc,
|
||||
const char *option_name)
|
||||
: context (option_name, affected_location_mgr),
|
||||
m_dc (dc),
|
||||
m_control_location_mgr (control_location_mgr),
|
||||
m_loc (loc)
|
||||
{}
|
||||
|
||||
void report_error_va (const char *gmsgid, va_list *ap) const final override
|
||||
ATTRIBUTE_GCC_DIAG(2, 0)
|
||||
{
|
||||
m_dc.begin_group ();
|
||||
rich_location richloc (m_control_location_mgr, m_loc);
|
||||
m_dc.diagnostic_impl (&richloc, nullptr, -1, gmsgid, ap, DK_ERROR);
|
||||
m_dc.end_group ();
|
||||
}
|
||||
|
||||
diagnostic_context &m_dc;
|
||||
line_maps *m_control_location_mgr;
|
||||
location_t m_loc;
|
||||
};
|
||||
|
||||
} // namespace diagnostics_output_spec
|
||||
|
||||
#endif
|
||||
|
|
@ -524,6 +524,13 @@ diagnostic_context::supports_fnotice_on_stderr_p () const
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_context::set_main_input_filename (const char *filename)
|
||||
{
|
||||
for (auto sink : m_output_sinks)
|
||||
sink->set_main_input_filename (filename);
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_context::
|
||||
set_client_data_hooks (std::unique_ptr<diagnostic_client_data_hooks> hooks)
|
||||
|
|
|
|||
|
|
@ -840,6 +840,8 @@ public:
|
|||
return m_option_classifier.m_classification_history;
|
||||
}
|
||||
|
||||
void set_main_input_filename (const char *filename);
|
||||
|
||||
private:
|
||||
void error_recursion () ATTRIBUTE_NORETURN;
|
||||
|
||||
|
|
|
|||
|
|
@ -177,3 +177,12 @@ acccessing values within a :type:`diagnostic_logical_location`:
|
|||
* :func:`diagnostic_logical_location_get_fully_qualified_name`
|
||||
|
||||
* :func:`diagnostic_logical_location_get_decorated_name`
|
||||
|
||||
``LIBGDIAGNOSTICS_ABI_2``
|
||||
-------------------------
|
||||
``LIBGDIAGNOSTICS_ABI_2`` covers the addition of these functions for
|
||||
supporting command-line options and SARIF playback:
|
||||
|
||||
* :func:`diagnostic_manager_add_sink_from_spec`
|
||||
|
||||
* :func:`diagnostic_manager_set_analysis_target`
|
||||
|
|
|
|||
|
|
@ -56,3 +56,45 @@ Responsibilities include:
|
|||
This will flush output to all of the output sinks, and clean up.
|
||||
|
||||
The parameter must be non-NULL.
|
||||
|
||||
.. function:: int diagnostic_manager_add_sink_from_spec (diagnostic_manager *affected_mgr, \
|
||||
const char *option_name, \
|
||||
const char *spec, \
|
||||
diagnostic_manager *control_mgr)
|
||||
|
||||
This function can be used to support option processing similar to GCC's
|
||||
:option:`-fdiagnostics-add-output=`. This allows command-line tools to
|
||||
support the same domain-specific language for specifying output sink
|
||||
as GCC does.
|
||||
|
||||
The function will attempt to parse :param:`spec` as if it were
|
||||
an argument to GCC's :option:`-fdiagnostics-add-output=OUTPUT-SPEC`.
|
||||
If successful, it will add an output sink to :param:`affected_mgr` and return zero.
|
||||
Otherwise, it will emit an error diagnostic to :param:`control_mgr` and
|
||||
return non-zero.
|
||||
|
||||
:param:`affected_mgr` and :param:`control_mgr` can be the same manager,
|
||||
or be different managers.
|
||||
|
||||
This function was added in :ref:`LIBGDIAGNOSTICS_ABI_2`; you can
|
||||
test for its presence using
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#ifdef LIBDIAGNOSTICS_HAVE_diagnostic_manager_add_sink_from_spec
|
||||
|
||||
|
||||
.. function:: void diagnostic_manager_set_analysis_target (diagnostic_manager *mgr, \
|
||||
const diagnostic_file *file)
|
||||
|
||||
This function sets the "main input file" of :param:`mgr` to be
|
||||
:param:`file`.
|
||||
This affects the :code:`<title>` of generated HTML and
|
||||
the :code:`role` of the artifact in SARIF output (SARIF v2.1.0 section 3.24.6).
|
||||
|
||||
This function was added in :ref:`LIBGDIAGNOSTICS_ABI_2`; you can
|
||||
test for its presence using
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#ifdef LIBDIAGNOSTICS_HAVE_diagnostic_manager_set_analysis_target
|
||||
|
|
|
|||
|
|
@ -329,6 +329,17 @@ public:
|
|||
version);
|
||||
}
|
||||
|
||||
bool
|
||||
add_sink_from_spec (const char *option_name,
|
||||
const char *spec,
|
||||
manager control_mgr)
|
||||
{
|
||||
return diagnostic_manager_add_sink_from_spec (m_inner,
|
||||
option_name,
|
||||
spec,
|
||||
control_mgr.m_inner);
|
||||
}
|
||||
|
||||
void
|
||||
write_patch (FILE *dst_stream)
|
||||
{
|
||||
|
|
@ -381,6 +392,8 @@ public:
|
|||
diagnostic
|
||||
begin_diagnostic (enum diagnostic_level level);
|
||||
|
||||
void
|
||||
set_analysis_target (file f);
|
||||
|
||||
diagnostic_manager *m_inner;
|
||||
bool m_owned;
|
||||
|
|
@ -683,6 +696,12 @@ manager::begin_diagnostic (enum diagnostic_level level)
|
|||
return diagnostic (diagnostic_begin (m_inner, level));
|
||||
}
|
||||
|
||||
inline void
|
||||
manager::set_analysis_target (file f)
|
||||
{
|
||||
diagnostic_manager_set_analysis_target (m_inner, f.m_inner);
|
||||
}
|
||||
|
||||
} // namespace libgdiagnostics
|
||||
|
||||
#endif // #ifndef LIBGDIAGNOSTICSPP_H
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "diagnostic-client-data-hooks.h"
|
||||
#include "diagnostic-format-sarif.h"
|
||||
#include "diagnostic-format-text.h"
|
||||
#include "diagnostic-output-spec.h"
|
||||
#include "logical-location.h"
|
||||
#include "edit-context.h"
|
||||
#include "libgdiagnostics.h"
|
||||
|
|
@ -1983,3 +1984,67 @@ diagnostic_logical_location_get_decorated_name (const diagnostic_logical_locatio
|
|||
|
||||
return loc->m_decorated_name.get_str ();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct spec_context : public diagnostics_output_spec::context
|
||||
{
|
||||
public:
|
||||
spec_context (const char *option_name,
|
||||
diagnostic_manager &affected_mgr,
|
||||
diagnostic_manager &control_mgr)
|
||||
: context (option_name, affected_mgr.get_line_table ()),
|
||||
m_control_mgr (control_mgr)
|
||||
{}
|
||||
|
||||
void report_error_va (const char *gmsgid, va_list *ap) const final override
|
||||
{
|
||||
diagnostic *diag
|
||||
= diagnostic_begin (&m_control_mgr, DIAGNOSTIC_LEVEL_ERROR);
|
||||
diagnostic_finish_va (diag, gmsgid, ap);
|
||||
}
|
||||
|
||||
const char *
|
||||
get_base_filename () const final override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
diagnostic_manager &m_control_mgr;
|
||||
};
|
||||
|
||||
} // anon namespace
|
||||
|
||||
/* Public entrypoint. */
|
||||
|
||||
int
|
||||
diagnostic_manager_add_sink_from_spec (diagnostic_manager *affected_mgr,
|
||||
const char *option_name,
|
||||
const char *spec,
|
||||
diagnostic_manager *control_mgr)
|
||||
{
|
||||
FAIL_IF_NULL (affected_mgr);
|
||||
FAIL_IF_NULL (option_name);
|
||||
FAIL_IF_NULL (spec);
|
||||
FAIL_IF_NULL (control_mgr);
|
||||
|
||||
spec_context ctxt (option_name, *affected_mgr, *control_mgr);
|
||||
auto inner_sink = ctxt.parse_and_make_sink (spec, affected_mgr->get_dc ());
|
||||
if (!inner_sink)
|
||||
return -1;
|
||||
affected_mgr->get_dc ().add_sink (std::move (inner_sink));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Public entrypoint. */
|
||||
|
||||
void
|
||||
diagnostic_manager_set_analysis_target (diagnostic_manager *mgr,
|
||||
const diagnostic_file *file)
|
||||
{
|
||||
FAIL_IF_NULL (mgr);
|
||||
FAIL_IF_NULL (file);
|
||||
|
||||
mgr->get_dc ().set_main_input_filename (file->get_name ());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -734,6 +734,37 @@ extern diagnostic_file *
|
|||
diagnostic_physical_location_get_file (const diagnostic_physical_location *physical_loc)
|
||||
LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL(0);
|
||||
|
||||
/* Attempt to parse SPEC as if an argument to GCC's
|
||||
-fdiagnostics-add-output=OUTPUT-SPEC.
|
||||
If successful, add an output sink to AFFECTED_MGR and return zero.
|
||||
Otherwise, emit a diagnostic to CONTROL_MGR and return non-zero.
|
||||
Added in LIBGDIAGNOSTICS_ABI_2. */
|
||||
#define LIBDIAGNOSTICS_HAVE_diagnostic_manager_add_sink_from_spec
|
||||
|
||||
extern int
|
||||
diagnostic_manager_add_sink_from_spec (diagnostic_manager *affected_mgr,
|
||||
const char *option_name,
|
||||
const char *spec,
|
||||
diagnostic_manager *control_mgr)
|
||||
LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
|
||||
LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3)
|
||||
LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (4);
|
||||
|
||||
|
||||
/* Set the main input file of MGR to be FILE.
|
||||
This affects the <title> of generated HTML and
|
||||
the "role" of the artifact in SARIF output (SARIF v2.1.0
|
||||
section 3.24.6).
|
||||
Added in LIBGDIAGNOSTICS_ABI_2. */
|
||||
#define LIBDIAGNOSTICS_HAVE_diagnostic_manager_set_analysis_target
|
||||
|
||||
extern void
|
||||
diagnostic_manager_set_analysis_target (diagnostic_manager *mgr,
|
||||
const diagnostic_file *file)
|
||||
LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1)
|
||||
LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2);
|
||||
|
||||
/* DEFERRED:
|
||||
- thread-safety
|
||||
- plural forms
|
||||
|
|
|
|||
|
|
@ -83,3 +83,10 @@ LIBGDIAGNOSTICS_ABI_1 {
|
|||
diagnostic_logical_location_get_fully_qualified_name;
|
||||
diagnostic_logical_location_get_decorated_name;
|
||||
} LIBGDIAGNOSTICS_ABI_0;
|
||||
|
||||
# Add hooks needed for HTML output from sarif-replay
|
||||
LIBGDIAGNOSTICS_ABI_2 {
|
||||
global:
|
||||
diagnostic_manager_add_sink_from_spec;
|
||||
diagnostic_manager_set_analysis_target;
|
||||
} LIBGDIAGNOSTICS_ABI_1;
|
||||
|
|
|
|||
|
|
@ -966,6 +966,17 @@ sarif_replayer::handle_artifact_obj (const json::object &artifact_obj)
|
|||
auto file = m_output_mgr.new_file (artifact_loc_uri->get_string (),
|
||||
sarif_source_language);
|
||||
|
||||
// 3.24.6 "roles" property
|
||||
const property_spec_ref prop_roles
|
||||
("artifact", "roles", "3.24.6");
|
||||
if (auto roles_obj
|
||||
= get_optional_property<json::array> (artifact_obj,
|
||||
prop_roles))
|
||||
for (auto iter : *roles_obj)
|
||||
if (auto str = require_string (*iter, prop_roles))
|
||||
if (!strcmp (str->get_string (), "analysisTarget"))
|
||||
m_output_mgr.set_analysis_target (file);
|
||||
|
||||
// Set contents, if available
|
||||
const property_spec_ref prop_contents
|
||||
("artifact", "contents", "3.24.8");
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ along with GCC; see the file COPYING3. If not see
|
|||
|
||||
|
||||
/* This file implements the options -fdiagnostics-add-output=,
|
||||
-fdiagnostics-set-output=, and their domain-specific language. */
|
||||
-fdiagnostics-set-output=. Most of the work is done
|
||||
by diagnostic-output-spec.cc so it can be shared by libgdiagnostics. */
|
||||
|
||||
#include "config.h"
|
||||
#define INCLUDE_ARRAY
|
||||
|
|
@ -30,627 +31,42 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "version.h"
|
||||
#include "intl.h"
|
||||
#include "diagnostic.h"
|
||||
#include "diagnostic-color.h"
|
||||
#include "diagnostic-format.h"
|
||||
#include "diagnostic-format-html.h"
|
||||
#include "diagnostic-format-text.h"
|
||||
#include "diagnostic-format-sarif.h"
|
||||
#include "selftest.h"
|
||||
#include "selftest-diagnostic.h"
|
||||
#include "pretty-print-markup.h"
|
||||
#include "diagnostic-output-spec.h"
|
||||
#include "opts.h"
|
||||
#include "options.h"
|
||||
|
||||
/* A namespace for handling the DSL of the arguments of
|
||||
-fdiagnostics-add-output= and -fdiagnostics-set-output=. */
|
||||
|
||||
namespace gcc {
|
||||
namespace diagnostics_output_spec {
|
||||
|
||||
/* Decls. */
|
||||
|
||||
struct context
|
||||
namespace {
|
||||
|
||||
struct opt_spec_context : public diagnostics_output_spec::gcc_spec_context
|
||||
{
|
||||
public:
|
||||
context (const gcc_options &opts,
|
||||
diagnostic_context &dc,
|
||||
line_maps *location_mgr,
|
||||
location_t loc,
|
||||
const char *option_name)
|
||||
: m_opts (opts), m_dc (dc), m_location_mgr (location_mgr), m_loc (loc),
|
||||
m_option_name (option_name)
|
||||
opt_spec_context (const gcc_options &opts,
|
||||
diagnostic_context &dc,
|
||||
line_maps *location_mgr,
|
||||
location_t loc,
|
||||
const char *option_name)
|
||||
: gcc_spec_context (dc,
|
||||
location_mgr,
|
||||
location_mgr,
|
||||
loc,
|
||||
option_name),
|
||||
m_opts (opts)
|
||||
{}
|
||||
|
||||
void
|
||||
report_error (const char *gmsgid, ...) const
|
||||
ATTRIBUTE_GCC_DIAG(2,3);
|
||||
|
||||
void
|
||||
report_unknown_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &scheme_name,
|
||||
auto_vec<const char *> &known_keys) const;
|
||||
|
||||
void
|
||||
report_missing_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &scheme_name,
|
||||
const char *metavar) const;
|
||||
|
||||
diagnostic_output_file
|
||||
open_output_file (label_text &&filename) const;
|
||||
const char *
|
||||
get_base_filename () const final override
|
||||
{
|
||||
return (m_opts.x_dump_base_name
|
||||
? m_opts.x_dump_base_name
|
||||
: m_opts.x_main_input_basename);
|
||||
}
|
||||
|
||||
const gcc_options &m_opts;
|
||||
diagnostic_context &m_dc;
|
||||
line_maps *m_location_mgr;
|
||||
location_t m_loc;
|
||||
const char *m_option_name;
|
||||
};
|
||||
|
||||
struct scheme_name_and_params
|
||||
{
|
||||
std::string m_scheme_name;
|
||||
std::vector<std::pair<std::string, std::string>> m_kvs;
|
||||
};
|
||||
|
||||
static std::unique_ptr<scheme_name_and_params>
|
||||
parse (const context &ctxt, const char *unparsed_arg);
|
||||
|
||||
/* Class for parsing the arguments of -fdiagnostics-add-output= and
|
||||
-fdiagnostics-set-output=, and making diagnostic_output_format
|
||||
instances (or issuing errors). */
|
||||
|
||||
class output_factory
|
||||
{
|
||||
public:
|
||||
class scheme_handler
|
||||
{
|
||||
public:
|
||||
scheme_handler (std::string scheme_name)
|
||||
: m_scheme_name (std::move (scheme_name))
|
||||
{}
|
||||
virtual ~scheme_handler () {}
|
||||
|
||||
const std::string &get_scheme_name () const { return m_scheme_name; }
|
||||
|
||||
virtual std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const = 0;
|
||||
|
||||
protected:
|
||||
bool
|
||||
parse_bool_value (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &value,
|
||||
bool &out) const
|
||||
{
|
||||
if (value == "yes")
|
||||
{
|
||||
out = true;
|
||||
return true;
|
||||
}
|
||||
else if (value == "no")
|
||||
{
|
||||
out = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctxt.report_error
|
||||
("%<%s%s%>:"
|
||||
" unexpected value %qs for key %qs; expected %qs or %qs",
|
||||
ctxt.m_option_name, unparsed_arg,
|
||||
value.c_str (),
|
||||
key.c_str (),
|
||||
"yes", "no");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
template <typename EnumType, size_t NumValues>
|
||||
bool
|
||||
parse_enum_value (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &value,
|
||||
const std::array<std::pair<const char *, EnumType>, NumValues> &value_names,
|
||||
EnumType &out) const
|
||||
{
|
||||
for (auto &iter : value_names)
|
||||
if (value == iter.first)
|
||||
{
|
||||
out = iter.second;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto_vec<const char *> known_values;
|
||||
for (auto iter : value_names)
|
||||
known_values.safe_push (iter.first);
|
||||
pp_markup::comma_separated_quoted_strings e (known_values);
|
||||
ctxt.report_error
|
||||
("%<%s%s%>:"
|
||||
" unexpected value %qs for key %qs; known values: %e",
|
||||
ctxt.m_option_name, unparsed_arg,
|
||||
value.c_str (),
|
||||
key.c_str (),
|
||||
&e);
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string m_scheme_name;
|
||||
};
|
||||
|
||||
output_factory ();
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg);
|
||||
|
||||
const scheme_handler *get_scheme_handler (const std::string &scheme_name);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<scheme_handler>> m_scheme_handlers;
|
||||
};
|
||||
|
||||
class text_scheme_handler : public output_factory::scheme_handler
|
||||
{
|
||||
public:
|
||||
text_scheme_handler () : scheme_handler ("text") {}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const final override;
|
||||
};
|
||||
|
||||
class sarif_scheme_handler : public output_factory::scheme_handler
|
||||
{
|
||||
public:
|
||||
sarif_scheme_handler () : scheme_handler ("sarif") {}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const final override;
|
||||
};
|
||||
|
||||
class html_scheme_handler : public output_factory::scheme_handler
|
||||
{
|
||||
public:
|
||||
html_scheme_handler () : scheme_handler ("experimental-html") {}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const final override;
|
||||
};
|
||||
|
||||
/* struct context. */
|
||||
|
||||
void
|
||||
context::report_error (const char *gmsgid, ...) const
|
||||
{
|
||||
m_dc.begin_group ();
|
||||
va_list ap;
|
||||
va_start (ap, gmsgid);
|
||||
rich_location richloc (m_location_mgr, m_loc);
|
||||
m_dc.diagnostic_impl (&richloc, nullptr, -1, gmsgid, &ap, DK_ERROR);
|
||||
va_end (ap);
|
||||
m_dc.end_group ();
|
||||
}
|
||||
|
||||
void
|
||||
context::report_unknown_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &scheme_name,
|
||||
auto_vec<const char *> &known_keys) const
|
||||
{
|
||||
pp_markup::comma_separated_quoted_strings e (known_keys);
|
||||
report_error
|
||||
("%<%s%s%>:"
|
||||
" unknown key %qs for format %qs; known keys: %e",
|
||||
m_option_name, unparsed_arg,
|
||||
key.c_str (), scheme_name.c_str (), &e);
|
||||
}
|
||||
|
||||
void
|
||||
context::report_missing_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &scheme_name,
|
||||
const char *metavar) const
|
||||
{
|
||||
report_error
|
||||
("%<%s%s%>:"
|
||||
" missing required key %qs for format %qs;"
|
||||
" try %<%s%s:%s=%s%>",
|
||||
m_option_name, unparsed_arg,
|
||||
key.c_str (), scheme_name.c_str (),
|
||||
m_option_name, scheme_name.c_str (), key.c_str (), metavar);
|
||||
}
|
||||
|
||||
std::unique_ptr<scheme_name_and_params>
|
||||
parse (const context &ctxt, const char *unparsed_arg)
|
||||
{
|
||||
scheme_name_and_params result;
|
||||
if (const char *const colon = strchr (unparsed_arg, ':'))
|
||||
{
|
||||
result.m_scheme_name = std::string (unparsed_arg, colon - unparsed_arg);
|
||||
/* Expect zero of more of KEY=VALUE,KEY=VALUE, etc .*/
|
||||
const char *iter = colon + 1;
|
||||
const char *last_separator = ":";
|
||||
while (iter)
|
||||
{
|
||||
/* Look for a non-empty key string followed by '='. */
|
||||
const char *eq = strchr (iter, '=');
|
||||
if (eq == nullptr || eq == iter)
|
||||
{
|
||||
/* Missing '='. */
|
||||
ctxt.report_error
|
||||
("%<%s%s%>:"
|
||||
" expected KEY=VALUE-style parameter for format %qs"
|
||||
" after %qs;"
|
||||
" got %qs",
|
||||
ctxt.m_option_name, unparsed_arg,
|
||||
result.m_scheme_name.c_str (),
|
||||
last_separator,
|
||||
iter);
|
||||
return nullptr;
|
||||
}
|
||||
std::string key = std::string (iter, eq - iter);
|
||||
std::string value;
|
||||
const char *comma = strchr (iter, ',');
|
||||
if (comma)
|
||||
{
|
||||
value = std::string (eq + 1, comma - (eq + 1));
|
||||
iter = comma + 1;
|
||||
last_separator = ",";
|
||||
}
|
||||
else
|
||||
{
|
||||
value = std::string (eq + 1);
|
||||
iter = nullptr;
|
||||
}
|
||||
result.m_kvs.push_back ({std::move (key), std::move (value)});
|
||||
}
|
||||
}
|
||||
else
|
||||
result.m_scheme_name = unparsed_arg;
|
||||
return std::make_unique<scheme_name_and_params> (std::move (result));
|
||||
}
|
||||
|
||||
/* class output_factory::scheme_handler. */
|
||||
|
||||
/* class output_factory. */
|
||||
|
||||
output_factory::output_factory ()
|
||||
{
|
||||
m_scheme_handlers.push_back (std::make_unique<text_scheme_handler> ());
|
||||
m_scheme_handlers.push_back (std::make_unique<sarif_scheme_handler> ());
|
||||
m_scheme_handlers.push_back (std::make_unique<html_scheme_handler> ());
|
||||
}
|
||||
|
||||
const output_factory::scheme_handler *
|
||||
output_factory::get_scheme_handler (const std::string &scheme_name)
|
||||
{
|
||||
for (auto &iter : m_scheme_handlers)
|
||||
if (iter->get_scheme_name () == scheme_name)
|
||||
return iter.get ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
output_factory::make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg)
|
||||
{
|
||||
auto scheme_handler = get_scheme_handler (parsed_arg.m_scheme_name);
|
||||
if (!scheme_handler)
|
||||
{
|
||||
auto_vec<const char *> strings;
|
||||
for (auto &iter : m_scheme_handlers)
|
||||
strings.safe_push (iter->get_scheme_name ().c_str ());
|
||||
pp_markup::comma_separated_quoted_strings e (strings);
|
||||
ctxt.report_error ("%<%s%s%>:"
|
||||
" unrecognized format %qs; known formats: %e",
|
||||
ctxt.m_option_name, unparsed_arg,
|
||||
parsed_arg.m_scheme_name.c_str (), &e);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return scheme_handler->make_sink (ctxt, unparsed_arg, parsed_arg);
|
||||
}
|
||||
|
||||
/* class text_scheme_handler : public output_factory::scheme_handler. */
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
text_scheme_handler::make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const
|
||||
{
|
||||
bool show_color = pp_show_color (ctxt.m_dc.get_reference_printer ());
|
||||
bool show_nesting = false;
|
||||
bool show_locations_in_nesting = true;
|
||||
bool show_levels = false;
|
||||
for (auto& iter : parsed_arg.m_kvs)
|
||||
{
|
||||
const std::string &key = iter.first;
|
||||
const std::string &value = iter.second;
|
||||
if (key == "color")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_color))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "experimental-nesting")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_nesting))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "experimental-nesting-show-locations")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_locations_in_nesting))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "experimental-nesting-show-levels")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_levels))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Key not found. */
|
||||
auto_vec<const char *> known_keys;
|
||||
known_keys.safe_push ("color");
|
||||
known_keys.safe_push ("experimental-nesting");
|
||||
known_keys.safe_push ("experimental-nesting-show-locations");
|
||||
known_keys.safe_push ("experimental-nesting-show-levels");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
|
||||
known_keys);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto sink = std::make_unique<diagnostic_text_output_format> (ctxt.m_dc);
|
||||
sink->set_show_nesting (show_nesting);
|
||||
sink->set_show_locations_in_nesting (show_locations_in_nesting);
|
||||
sink->set_show_nesting_levels (show_levels);
|
||||
return sink;
|
||||
}
|
||||
|
||||
diagnostic_output_file
|
||||
context::open_output_file (label_text &&filename) const
|
||||
{
|
||||
FILE *outf = fopen (filename.get (), "w");
|
||||
if (!outf)
|
||||
{
|
||||
rich_location richloc (m_location_mgr, m_loc);
|
||||
m_dc.emit_diagnostic_with_group
|
||||
(DK_ERROR, richloc, nullptr, 0,
|
||||
"unable to open %qs: %m", filename.get ());
|
||||
return diagnostic_output_file (nullptr, false, std::move (filename));
|
||||
}
|
||||
return diagnostic_output_file (outf, true, std::move (filename));
|
||||
}
|
||||
|
||||
/* class sarif_scheme_handler : public output_factory::scheme_handler. */
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
sarif_scheme_handler::make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const
|
||||
{
|
||||
label_text filename;
|
||||
enum sarif_serialization_kind serialization_kind
|
||||
= sarif_serialization_kind::json;
|
||||
enum sarif_version version = sarif_version::v2_1_0;
|
||||
bool xml_state = false;
|
||||
for (auto& iter : parsed_arg.m_kvs)
|
||||
{
|
||||
const std::string &key = iter.first;
|
||||
const std::string &value = iter.second;
|
||||
if (key == "file")
|
||||
{
|
||||
filename = label_text::take (xstrdup (value.c_str ()));
|
||||
continue;
|
||||
}
|
||||
if (key == "serialization")
|
||||
{
|
||||
static const std::array<std::pair<const char *, enum sarif_serialization_kind>,
|
||||
(size_t)sarif_serialization_kind::num_values> value_names
|
||||
{{{"json", sarif_serialization_kind::json}}};
|
||||
|
||||
if (!parse_enum_value<enum sarif_serialization_kind>
|
||||
(ctxt, unparsed_arg,
|
||||
key, value,
|
||||
value_names,
|
||||
serialization_kind))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "version")
|
||||
{
|
||||
static const std::array<std::pair<const char *, enum sarif_version>,
|
||||
(size_t)sarif_version::num_versions> value_names
|
||||
{{{"2.1", sarif_version::v2_1_0},
|
||||
{"2.2-prerelease", sarif_version::v2_2_prerelease_2024_08_08}}};
|
||||
|
||||
if (!parse_enum_value<enum sarif_version> (ctxt, unparsed_arg,
|
||||
key, value,
|
||||
value_names,
|
||||
version))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "xml-state")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
xml_state))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Key not found. */
|
||||
auto_vec<const char *> known_keys;
|
||||
known_keys.safe_push ("file");
|
||||
known_keys.safe_push ("serialization");
|
||||
known_keys.safe_push ("version");
|
||||
known_keys.safe_push ("xml-state");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
|
||||
known_keys);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
diagnostic_output_file output_file;
|
||||
if (filename.get ())
|
||||
output_file = ctxt.open_output_file (std::move (filename));
|
||||
else
|
||||
// Default filename
|
||||
{
|
||||
const char *basename = (ctxt.m_opts.x_dump_base_name
|
||||
? ctxt.m_opts.x_dump_base_name
|
||||
: ctxt.m_opts.x_main_input_basename);
|
||||
output_file = diagnostic_output_format_open_sarif_file (ctxt.m_dc,
|
||||
line_table,
|
||||
basename,
|
||||
serialization_kind);
|
||||
}
|
||||
if (!output_file)
|
||||
return nullptr;
|
||||
|
||||
sarif_generation_options sarif_gen_opts;
|
||||
sarif_gen_opts.m_version = version;
|
||||
sarif_gen_opts.m_xml_state = xml_state;
|
||||
|
||||
std::unique_ptr<sarif_serialization_format> serialization_obj;
|
||||
switch (serialization_kind)
|
||||
{
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
case sarif_serialization_kind::json:
|
||||
serialization_obj
|
||||
= std::make_unique<sarif_serialization_format_json> (true);
|
||||
break;
|
||||
}
|
||||
|
||||
auto sink = make_sarif_sink (ctxt.m_dc,
|
||||
*line_table,
|
||||
std::move (serialization_obj),
|
||||
sarif_gen_opts,
|
||||
std::move (output_file));
|
||||
return sink;
|
||||
}
|
||||
|
||||
/* class html_scheme_handler : public output_factory::scheme_handler. */
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
html_scheme_handler::make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const
|
||||
{
|
||||
bool css = true;
|
||||
label_text filename;
|
||||
bool javascript = true;
|
||||
bool show_state_diagrams = false;
|
||||
bool show_state_diagram_xml = false;
|
||||
bool show_state_diagram_dot_src = false;
|
||||
|
||||
for (auto& iter : parsed_arg.m_kvs)
|
||||
{
|
||||
const std::string &key = iter.first;
|
||||
const std::string &value = iter.second;
|
||||
if (key == "css")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
css))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "file")
|
||||
{
|
||||
filename = label_text::take (xstrdup (value.c_str ()));
|
||||
continue;
|
||||
}
|
||||
if (key == "javascript")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
javascript))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "show-state-diagrams")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_state_diagrams))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "show-state-diagram-dot-src")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_state_diagram_dot_src))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "show-state-diagram-xml")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
show_state_diagram_xml))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Key not found. */
|
||||
auto_vec<const char *> known_keys;
|
||||
known_keys.safe_push ("css");
|
||||
known_keys.safe_push ("file");
|
||||
known_keys.safe_push ("javascript");
|
||||
known_keys.safe_push ("show-state-diagrams");
|
||||
known_keys.safe_push ("show-state-diagram-dot-src");
|
||||
known_keys.safe_push ("show-state-diagram-xml");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
|
||||
known_keys);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
diagnostic_output_file output_file;
|
||||
if (filename.get ())
|
||||
output_file = ctxt.open_output_file (std::move (filename));
|
||||
else
|
||||
// Default filename
|
||||
{
|
||||
const char *basename = (ctxt.m_opts.x_dump_base_name
|
||||
? ctxt.m_opts.x_dump_base_name
|
||||
: ctxt.m_opts.x_main_input_basename);
|
||||
output_file = diagnostic_output_format_open_html_file (ctxt.m_dc,
|
||||
line_table,
|
||||
basename);
|
||||
}
|
||||
if (!output_file)
|
||||
return nullptr;
|
||||
|
||||
html_generation_options html_gen_opts;
|
||||
html_gen_opts.m_css = css;
|
||||
html_gen_opts.m_javascript = javascript;
|
||||
html_gen_opts.m_show_state_diagrams = show_state_diagrams;
|
||||
html_gen_opts.m_show_state_diagram_xml = show_state_diagram_xml;
|
||||
html_gen_opts.m_show_state_diagram_dot_src = show_state_diagram_dot_src;
|
||||
|
||||
auto sink = make_html_sink (ctxt.m_dc,
|
||||
*line_table,
|
||||
html_gen_opts,
|
||||
std::move (output_file));
|
||||
return sink;
|
||||
}
|
||||
|
||||
} // namespace diagnostics_output_spec
|
||||
} // namespace gcc
|
||||
} // anon namespace
|
||||
|
||||
void
|
||||
handle_OPT_fdiagnostics_add_output_ (const gcc_options &opts,
|
||||
|
|
@ -662,14 +78,8 @@ handle_OPT_fdiagnostics_add_output_ (const gcc_options &opts,
|
|||
gcc_assert (line_table);
|
||||
|
||||
const char *const option_name = "-fdiagnostics-add-output=";
|
||||
gcc::diagnostics_output_spec::context ctxt (opts, dc, line_table, loc,
|
||||
option_name);
|
||||
auto result = gcc::diagnostics_output_spec::parse (ctxt, arg);
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
gcc::diagnostics_output_spec::output_factory factory;
|
||||
auto sink = factory.make_sink (ctxt, arg, *result);
|
||||
opt_spec_context ctxt (opts, dc, line_table, loc, option_name);
|
||||
auto sink = ctxt.parse_and_make_sink (arg, dc);
|
||||
if (!sink)
|
||||
return;
|
||||
|
||||
|
|
@ -687,183 +97,11 @@ handle_OPT_fdiagnostics_set_output_ (const gcc_options &opts,
|
|||
gcc_assert (line_table);
|
||||
|
||||
const char *const option_name = "-fdiagnostics-set-output=";
|
||||
gcc::diagnostics_output_spec::context ctxt (opts, dc, line_table, loc,
|
||||
option_name);
|
||||
auto result = gcc::diagnostics_output_spec::parse (ctxt, arg);
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
gcc::diagnostics_output_spec::output_factory factory;
|
||||
auto sink = factory.make_sink (ctxt, arg, *result);
|
||||
opt_spec_context ctxt (opts, dc, line_table, loc, option_name);
|
||||
auto sink = ctxt.parse_and_make_sink (arg, dc);
|
||||
if (!sink)
|
||||
return;
|
||||
|
||||
sink->set_main_input_filename (opts.x_main_input_filename);
|
||||
dc.set_output_format (std::move (sink));
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
/* RAII class to temporarily override "progname" to the
|
||||
string "PROGNAME". */
|
||||
|
||||
class auto_fix_progname
|
||||
{
|
||||
public:
|
||||
auto_fix_progname ()
|
||||
{
|
||||
m_old_progname = progname;
|
||||
progname = "PROGNAME";
|
||||
}
|
||||
|
||||
~auto_fix_progname ()
|
||||
{
|
||||
progname = m_old_progname;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *m_old_progname;
|
||||
};
|
||||
|
||||
struct parser_test
|
||||
{
|
||||
parser_test ()
|
||||
: m_opts (),
|
||||
m_dc (),
|
||||
m_ctxt (m_opts, m_dc, line_table, UNKNOWN_LOCATION, "-fOPTION="),
|
||||
m_fmt (m_dc.get_output_format (0))
|
||||
{
|
||||
pp_buffer (m_fmt.get_printer ())->m_flush_p = false;
|
||||
}
|
||||
|
||||
std::unique_ptr<gcc::diagnostics_output_spec::scheme_name_and_params>
|
||||
parse (const char *unparsed_arg)
|
||||
{
|
||||
return gcc::diagnostics_output_spec::parse (m_ctxt, unparsed_arg);
|
||||
}
|
||||
|
||||
bool execution_failed_p () const
|
||||
{
|
||||
return m_dc.execution_failed_p ();
|
||||
}
|
||||
|
||||
const char *
|
||||
get_diagnostic_text () const
|
||||
{
|
||||
return pp_formatted_text (m_fmt.get_printer ());
|
||||
}
|
||||
|
||||
private:
|
||||
const gcc_options m_opts;
|
||||
test_diagnostic_context m_dc;
|
||||
gcc::diagnostics_output_spec::context m_ctxt;
|
||||
diagnostic_output_format &m_fmt;
|
||||
};
|
||||
|
||||
/* Selftests. */
|
||||
|
||||
static void
|
||||
test_output_arg_parsing ()
|
||||
{
|
||||
auto_fix_quotes fix_quotes;
|
||||
auto_fix_progname fix_progname;
|
||||
|
||||
/* Minimal correct example. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo");
|
||||
ASSERT_EQ (result->m_scheme_name, "foo");
|
||||
ASSERT_EQ (result->m_kvs.size (), 0);
|
||||
ASSERT_FALSE (pt.execution_failed_p ());
|
||||
}
|
||||
|
||||
/* Stray trailing colon with no key/value pairs. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `:';"
|
||||
" got `'\n");
|
||||
}
|
||||
|
||||
/* No key before '='. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:=");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:=':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `:';"
|
||||
" got `='\n");
|
||||
}
|
||||
|
||||
/* No value for key. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:key,");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:key,':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `:';"
|
||||
" got `key,'\n");
|
||||
}
|
||||
|
||||
/* Correct example, with one key/value pair. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:key=value");
|
||||
ASSERT_EQ (result->m_scheme_name, "foo");
|
||||
ASSERT_EQ (result->m_kvs.size (), 1);
|
||||
ASSERT_EQ (result->m_kvs[0].first, "key");
|
||||
ASSERT_EQ (result->m_kvs[0].second, "value");
|
||||
ASSERT_FALSE (pt.execution_failed_p ());
|
||||
}
|
||||
|
||||
/* Stray trailing comma. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:key=value,");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:key=value,':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `,';"
|
||||
" got `'\n");
|
||||
}
|
||||
|
||||
/* Correct example, with two key/value pairs. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:color=red,shape=circle");
|
||||
ASSERT_EQ (result->m_scheme_name, "foo");
|
||||
ASSERT_EQ (result->m_kvs.size (), 2);
|
||||
ASSERT_EQ (result->m_kvs[0].first, "color");
|
||||
ASSERT_EQ (result->m_kvs[0].second, "red");
|
||||
ASSERT_EQ (result->m_kvs[1].first, "shape");
|
||||
ASSERT_EQ (result->m_kvs[1].second, "circle");
|
||||
ASSERT_FALSE (pt.execution_failed_p ());
|
||||
}
|
||||
}
|
||||
|
||||
/* Run all of the selftests within this file. */
|
||||
|
||||
void
|
||||
opts_diagnostic_cc_tests ()
|
||||
{
|
||||
test_output_arg_parsing ();
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
#endif /* #if CHECKING_P */
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ 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"
|
||||
|
|
@ -48,6 +49,7 @@ struct options
|
|||
|
||||
replay_options m_replay_opts;
|
||||
std::vector<const char *> m_sarif_filenames;
|
||||
std::vector<std::string> m_extra_output_specs;
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
@ -70,6 +72,10 @@ static const char *const usage_msg = (
|
|||
"\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"
|
||||
|
|
@ -95,6 +101,19 @@ 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,
|
||||
|
|
@ -128,6 +147,13 @@ parse_options (int argc, char **argv,
|
|||
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;
|
||||
|
|
@ -221,6 +247,12 @@ main (int argc, char **argv)
|
|||
libgdiagnostics::manager playback_mgr;
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ selftest::run_tests ()
|
|||
diagnostic_format_html_cc_tests ();
|
||||
diagnostic_format_json_cc_tests ();
|
||||
diagnostic_format_sarif_cc_tests ();
|
||||
diagnostic_output_spec_cc_tests ();
|
||||
edit_context_cc_tests ();
|
||||
fold_const_cc_tests ();
|
||||
spellcheck_cc_tests ();
|
||||
|
|
@ -112,7 +113,6 @@ selftest::run_tests ()
|
|||
simple_diagnostic_path_cc_tests ();
|
||||
lazy_diagnostic_path_cc_tests ();
|
||||
attribs_cc_tests ();
|
||||
opts_diagnostic_cc_tests ();
|
||||
path_coverage_cc_tests ();
|
||||
|
||||
/* This one relies on most of the above. */
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ extern void diagnostic_color_cc_tests ();
|
|||
extern void diagnostic_format_html_cc_tests ();
|
||||
extern void diagnostic_format_json_cc_tests ();
|
||||
extern void diagnostic_format_sarif_cc_tests ();
|
||||
extern void diagnostic_output_spec_cc_tests ();
|
||||
extern void diagnostic_path_output_cc_tests ();
|
||||
extern void diagnostic_show_locus_cc_tests ();
|
||||
extern void digraph_cc_tests ();
|
||||
|
|
@ -250,7 +251,6 @@ extern void lazy_diagnostic_path_cc_tests ();
|
|||
extern void opt_suggestions_cc_tests ();
|
||||
extern void optinfo_emit_json_cc_tests ();
|
||||
extern void opts_cc_tests ();
|
||||
extern void opts_diagnostic_cc_tests ();
|
||||
extern void ordered_hash_map_tests_cc_tests ();
|
||||
extern void path_coverage_cc_tests ();
|
||||
extern void predict_cc_tests ();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
from htmltest import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def html_tree():
|
||||
return html_tree_from_env()
|
||||
|
||||
def test_generated_html(html_tree):
|
||||
root = html_tree.getroot ()
|
||||
assert root.tag == make_tag('html')
|
||||
|
||||
head = root.find('xhtml:head', ns)
|
||||
assert head is not None
|
||||
|
||||
title = head.find('xhtml:title', ns)
|
||||
assert title.text == '../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c'
|
||||
|
||||
diag = get_diag_by_index(html_tree, 0)
|
||||
|
||||
msg = get_message_within_diag(diag)
|
||||
assert msg is not None
|
||||
|
||||
assert_tag(msg[0], 'strong')
|
||||
assert msg[0].text == 'warning: '
|
||||
assert msg[0].tail == " call to ‘fprintf’ from within signal handler "
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
def test_basics(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json"
|
||||
|
||||
version = sarif['version']
|
||||
assert version == "2.1.0"
|
||||
|
||||
def test_execution_successful(sarif):
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
invocations = run['invocations']
|
||||
assert len(invocations) == 1
|
||||
invocation = invocations[0]
|
||||
|
||||
assert invocation['executionSuccessful'] == True
|
||||
|
||||
def test_warning(sarif):
|
||||
result = get_result_by_index(sarif, 0)
|
||||
|
||||
assert result['level'] == 'warning'
|
||||
|
||||
# TODO: this should be "-Wanalyzer-unsafe-call-within-signal-handler" and have a URL
|
||||
assert result['ruleId'] == 'warning'
|
||||
|
||||
# TODO: check code flow
|
||||
events = result["codeFlows"][0]["threadFlows"][0]['locations']
|
||||
|
||||
# Event "(1)": "entry to 'main'" (index == 0)
|
||||
assert events[0]['location']['message']['text'] == "entry to ‘main’"
|
||||
|
||||
# Final event:
|
||||
assert events[-1]['location']['message']['text'].startswith("call to ‘fprintf’ from within signal handler")
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
The dg directives were stripped out from the generated .sarif
|
||||
to avoid confusing DejaGnu for this test. */
|
||||
/* { dg-additional-options "-fdiagnostics-add-output=experimental-html:file=signal-1.c.sarif.html,javascript=no" } */
|
||||
/* { dg-additional-options "-fdiagnostics-add-output=sarif:file=signal-1.c.roundtrip.sarif" } */
|
||||
|
||||
{"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
||||
"version": "2.1.0",
|
||||
|
|
@ -211,3 +213,11 @@ In function 'custom_logger':
|
|||
| | (7) call to ‘fprintf’ from within signal handler
|
||||
|
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.html file:
|
||||
{ dg-final { run-html-pytest signal-1.c.sarif "2.1.0-valid/signal-1-check-html.py" } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the *generated*
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest signal-1.c.roundtrip "2.1.0-valid/signal-1-check-sarif-roundtrip.py" } } */
|
||||
|
|
|
|||
Loading…
Reference in New Issue