diff options
Diffstat (limited to 'gcc/opts-diagnostic.cc')
-rw-r--r-- | gcc/opts-diagnostic.cc | 758 |
1 files changed, 31 insertions, 727 deletions
diff --git a/gcc/opts-diagnostic.cc b/gcc/opts-diagnostic.cc index 34c3906..d2c3a93 100644 --- a/gcc/opts-diagnostic.cc +++ b/gcc/opts-diagnostic.cc @@ -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,563 +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 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 + const char * + get_base_filename () const final override { - 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") {} + return (m_opts.x_dump_base_name + ? m_opts.x_dump_base_name + : m_opts.x_main_input_basename); + } - 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; + const gcc_options &m_opts; }; -/* 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; - 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; - } - - /* Key not found. */ - auto_vec<const char *> known_keys; - known_keys.safe_push ("file"); - known_keys.safe_push ("serialization"); - known_keys.safe_push ("version"); - 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; - - 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, - ctxt.m_opts.x_main_input_filename, - 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 -{ - label_text filename; - 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; - } - - /* Key not found. */ - auto_vec<const char *> known_keys; - known_keys.safe_push ("file"); - 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; - - auto sink = make_html_sink (ctxt.m_dc, - *line_table, - std::move (output_file)); - return sink; -} - -} // namespace diagnostics_output_spec -} // namespace gcc +} // anon namespace void handle_OPT_fdiagnostics_add_output_ (const gcc_options &opts, @@ -598,17 +78,12 @@ 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; + sink->set_main_input_filename (opts.x_main_input_filename); dc.add_sink (std::move (sink)); } @@ -622,182 +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 */ |