LCOV - code coverage report
Current view: top level - gcc/diagnostics - output-spec.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 74.5 % 369 275
Test Date: 2026-02-28 14:20:25 Functions: 75.8 % 33 25
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Support for the DSL of -fdiagnostics-add-output= and
       2              :    -fdiagnostics-set-output=.
       3              :    Copyright (C) 2024-2026 Free Software Foundation, Inc.
       4              : 
       5              : This file is part of GCC.
       6              : 
       7              : GCC is free software; you can redistribute it and/or modify it under
       8              : the terms of the GNU General Public License as published by the Free
       9              : Software Foundation; either version 3, or (at your option) any later
      10              : version.
      11              : 
      12              : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13              : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14              : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15              : for more details.
      16              : 
      17              : You should have received a copy of the GNU General Public License
      18              : along with GCC; see the file COPYING3.  If not see
      19              : <http://www.gnu.org/licenses/>.  */
      20              : 
      21              : /* This file implements the domain-specific language for the options
      22              :    -fdiagnostics-add-output= and -fdiagnostics-set-output=, and for
      23              :    the "diagnostic_manager_add_sink_from_spec" entrypoint to
      24              :    libgdiagnostics.  */
      25              : 
      26              : #include "config.h"
      27              : #define INCLUDE_ARRAY
      28              : #define INCLUDE_STRING
      29              : #define INCLUDE_VECTOR
      30              : #include "system.h"
      31              : #include "coretypes.h"
      32              : #include "version.h"
      33              : #include "intl.h"
      34              : #include "diagnostics/color.h"
      35              : #include "diagnostics/sink.h"
      36              : #include "diagnostics/html-sink.h"
      37              : #include "diagnostics/text-sink.h"
      38              : #include "diagnostics/sarif-sink.h"
      39              : #include "selftest.h"
      40              : #include "diagnostics/selftest-context.h"
      41              : #include "pretty-print-markup.h"
      42              : #include "diagnostics/output-spec.h"
      43              : 
      44              : /* A namespace for handling the DSL of the arguments of
      45              :    -fdiagnostics-add-output= and -fdiagnostics-set-output=
      46              :    which look like:
      47              :      SCHEME[:KEY=VALUE(,KEY=VALUE)*]
      48              :    We call this an output spec.  */
      49              : 
      50              : namespace diagnostics {
      51              : namespace output_spec {
      52              : 
      53              : class scheme_handler;
      54              : 
      55              : /* Decls.  */
      56              : 
      57              : /* A class for the result of the first stage of parsing an output spec,
      58              :    where values are represented as untyped strings.
      59              :    The scheme might not exist.
      60              :    The keys have not been validated against the scheme.
      61              :    The values have not been validated against their keys.  */
      62              : 
      63          148 : struct scheme_name_and_params
      64              : {
      65              :   std::string m_scheme_name;
      66              :   std::vector<std::pair<std::string, std::string>> m_kvs;
      67              : };
      68              : 
      69              : /* Class for parsing the arguments of -fdiagnostics-add-output= and
      70              :    -fdiagnostics-set-output=, and making sink
      71              :    instances (or issuing errors).  */
      72              : 
      73          108 : class output_factory
      74              : {
      75              : public:
      76              :   output_factory (diagnostics::context &dc);
      77              : 
      78              :   std::unique_ptr<sink>
      79              :   make_sink (const context &ctxt,
      80              :              diagnostics::context &dc,
      81              :              const scheme_name_and_params &scheme_and_kvs);
      82              : 
      83              :   scheme_handler *get_scheme_handler (const std::string &scheme_name);
      84              : 
      85              : private:
      86              :   std::vector<std::unique_ptr<scheme_handler>> m_scheme_handlers;
      87              : };
      88              : 
      89              : enum key_handler::result
      90           49 : key_handler::parse_bool_value (const context &ctxt,
      91              :                                const std::string &key,
      92              :                                const std::string &value,
      93              :                                bool &out) const
      94              : {
      95           49 :   if (value == "yes")
      96              :     {
      97           22 :       out = true;
      98           22 :       return result::ok;
      99              :     }
     100           27 :   else if (value == "no")
     101              :     {
     102           27 :       out = false;
     103           27 :       return result::ok;
     104              :     }
     105              :   else
     106              :     {
     107            0 :       ctxt.report_error
     108            0 :         ("%<%s%s%>:"
     109              :          " unexpected value %qs for key %qs; expected %qs or %qs",
     110              :          ctxt.get_option_name (), ctxt.get_unparsed_spec (),
     111              :          value.c_str (),
     112              :          key.c_str (),
     113              :          "yes", "no");
     114            0 :       return result::malformed_value;
     115              :     }
     116              : }
     117              : 
     118              : template <typename EnumType, size_t NumValues>
     119              : key_handler::result
     120            3 : key_handler::parse_enum_value (const context &ctxt,
     121              :                                const std::string &key,
     122              :                                const std::string &value,
     123              :                                const std::array<std::pair<const char *,
     124              :                                                           EnumType>,
     125              :                                                 NumValues> &value_names,
     126              :                                EnumType &out) const
     127              : {
     128            5 :   for (auto &iter : value_names)
     129            5 :     if (value == iter.first)
     130              :       {
     131            3 :         out = iter.second;
     132            3 :         return result::ok;
     133              :       }
     134              : 
     135            0 :   auto_vec<const char *> known_values;
     136            0 :   for (auto iter : value_names)
     137            0 :     known_values.safe_push (iter.first);
     138            0 :   pp_markup::comma_separated_quoted_strings e (known_values);
     139              :   ctxt.report_error
     140            0 :     ("%<%s%s%>:"
     141              :      " unexpected value %qs for key %qs; known values: %e",
     142              :      ctxt.get_option_name (), ctxt.get_unparsed_spec (),
     143              :      value.c_str (),
     144              :      key.c_str (),
     145              :      &e);
     146            0 :   return result::malformed_value;
     147            0 : }
     148              : 
     149              : class text_scheme_handler : public scheme_handler
     150              : {
     151              : public:
     152           54 :   text_scheme_handler (diagnostics::context &dc)
     153           54 :   : scheme_handler ("text"),
     154           54 :     m_show_color (pp_show_color (dc.get_reference_printer ())),
     155           54 :     m_show_nesting (true),
     156           54 :     m_show_locations_in_nesting (true),
     157          108 :     m_show_levels (false)
     158              :   {
     159           54 :   }
     160              : 
     161              :   std::unique_ptr<sink>
     162              :   make_sink (const context &ctxt,
     163              :              diagnostics::context &dc) final override;
     164              : 
     165              :   enum result
     166              :   maybe_handle_kv (const context &ctxt,
     167              :                    const std::string &key,
     168              :                    const std::string &value) final override;
     169              : 
     170              :   void
     171              :   get_keys (auto_vec<const char *> &out) const final override;
     172              : 
     173              : private:
     174              :   bool m_show_color;
     175              :   bool m_show_nesting;
     176              :   bool m_show_locations_in_nesting;
     177              :   bool m_show_levels;
     178              : };
     179              : 
     180              : class sarif_scheme_handler : public scheme_handler
     181              : {
     182              : public:
     183           54 :   sarif_scheme_handler ()
     184           54 :   : scheme_handler ("sarif"),
     185          108 :     m_serialization_kind (sarif_serialization_kind::json)
     186              :   {
     187           54 :   }
     188              : 
     189              :   std::unique_ptr<sink>
     190              :   make_sink (const context &ctxt,
     191              :              diagnostics::context &dc) final override;
     192              : 
     193              :   enum result
     194              :   maybe_handle_kv (const context &ctxt,
     195              :                    const std::string &key,
     196              :                    const std::string &value) final override;
     197              : 
     198              :   void
     199              :   get_keys (auto_vec<const char *> &out) const final override;
     200              : 
     201              : private:
     202              :   static std::unique_ptr<sarif_serialization_format>
     203              :   make_sarif_serialization_object (enum sarif_serialization_kind);
     204              : 
     205              :   label_text m_filename;
     206              :   enum sarif_serialization_kind m_serialization_kind;
     207              :   sarif_generation_options m_generation_opts;
     208              : };
     209              : 
     210              : class html_scheme_handler : public scheme_handler
     211              : {
     212              : public:
     213           54 :   html_scheme_handler () : scheme_handler ("experimental-html") {}
     214              : 
     215              :   std::unique_ptr<sink>
     216              :   make_sink (const context &ctxt,
     217              :              diagnostics::context &dc) final override;
     218              : 
     219              :   enum result
     220              :   maybe_handle_kv (const context &ctxt,
     221              :                    const std::string &key,
     222              :                    const std::string &value) final override;
     223              : 
     224              :   void
     225              :   get_keys (auto_vec<const char *> &out) const final override;
     226              : 
     227              : private:
     228              :   label_text m_filename;
     229              :   html_generation_options m_html_gen_opts;
     230              : };
     231              : 
     232              : /* struct context.  */
     233              : 
     234              : void
     235           16 : context::report_error (const char *gmsgid, ...) const
     236              : {
     237           16 :   va_list ap;
     238           16 :   va_start (ap, gmsgid);
     239           16 :   report_error_va (gmsgid, &ap);
     240           16 :   va_end (ap);
     241           16 : }
     242              : 
     243              : void
     244            0 : context::report_unknown_key (const std::string &key,
     245              :                              const scheme_handler &scheme) const
     246              : {
     247            0 :   auto_vec<const char *> scheme_key_vec;
     248            0 :   scheme.get_keys (scheme_key_vec);
     249              : 
     250            0 :   pp_markup::comma_separated_quoted_strings e_scheme_keys (scheme_key_vec);
     251              : 
     252            0 :   const char *scheme_name = scheme.get_scheme_name ().c_str ();
     253              : 
     254            0 :   if (m_client_keys)
     255              :     {
     256            0 :       auto_vec<const char *> client_key_vec;
     257            0 :       m_client_keys->get_keys (client_key_vec);
     258            0 :       if (!client_key_vec.is_empty ())
     259              :         {
     260            0 :           pp_markup::comma_separated_quoted_strings e_client_keys
     261            0 :             (client_key_vec);
     262            0 :           report_error
     263            0 :             ("%<%s%s%>:"
     264              :              " unknown key %qs for output scheme %qs;"
     265              :              " scheme keys: %e; client keys: %e",
     266              :              get_option_name (), get_unparsed_spec (),
     267              :              key.c_str (), scheme_name,
     268              :              &e_scheme_keys, &e_client_keys);
     269            0 :         }
     270            0 :     }
     271              : 
     272            0 :   report_error
     273            0 :     ("%<%s%s%>:"
     274              :      " unknown key %qs for output scheme %qs; scheme keys: %e",
     275              :      get_option_name (), get_unparsed_spec (),
     276              :      key.c_str (), scheme_name, &e_scheme_keys);
     277            0 : }
     278              : 
     279              : void
     280            0 : context::report_missing_key (const std::string &key,
     281              :                              const std::string &scheme_name,
     282              :                              const char *metavar) const
     283              : {
     284            0 :   report_error
     285            0 :     ("%<%s%s%>:"
     286              :      " missing required key %qs for format %qs;"
     287              :      " try %<%s%s:%s=%s%>",
     288              :      get_option_name (), get_unparsed_spec (),
     289              :      key.c_str (), scheme_name.c_str (),
     290              :      get_option_name (), scheme_name.c_str (), key.c_str (), metavar);
     291            0 : }
     292              : 
     293              : output_file
     294            2 : context::open_output_file (label_text &&filename) const
     295              : {
     296            2 :   FILE *outf = fopen (filename.get (), "w");
     297            2 :   if (!outf)
     298              :     {
     299            0 :       report_error ("unable to open %qs: %m", filename.get ());
     300            0 :       return output_file (nullptr, false, std::move (filename));
     301              :     }
     302            2 :   return output_file (outf, true, std::move (filename));
     303              : }
     304              : 
     305              : static std::unique_ptr<scheme_name_and_params>
     306           82 : parse (const context &ctxt)
     307              : {
     308           82 :   scheme_name_and_params result;
     309           82 :   const char *const unparsed_spec = ctxt.get_unparsed_spec ();
     310           82 :   if (const char *const colon = strchr (unparsed_spec, ':'))
     311              :     {
     312           60 :       result.m_scheme_name = std::string (unparsed_spec, colon - unparsed_spec);
     313              :       /* Expect zero of more of KEY=VALUE,KEY=VALUE, etc  .*/
     314           60 :       const char *iter = colon + 1;
     315           60 :       const char *last_separator = ":";
     316          130 :       while (iter)
     317              :         {
     318              :           /* Look for a non-empty key string followed by '='.  */
     319           86 :           const char *eq = strchr (iter, '=');
     320           86 :           if (eq == nullptr || eq == iter)
     321              :             {
     322              :               /* Missing '='.  */
     323           16 :               ctxt.report_error
     324           16 :                 ("%<%s%s%>:"
     325              :                  " expected KEY=VALUE-style parameter for format %qs"
     326              :                  " after %qs;"
     327              :                  " got %qs",
     328              :                  ctxt.get_option_name (), ctxt.get_unparsed_spec (),
     329              :                  result.m_scheme_name.c_str (),
     330              :                  last_separator,
     331              :                  iter);
     332           16 :               return nullptr;
     333              :             }
     334           70 :           std::string key = std::string (iter, eq - iter);
     335           70 :           std::string value;
     336           70 :           const char *comma = strchr (iter, ',');
     337           70 :           if (comma)
     338              :             {
     339           26 :               value = std::string (eq + 1, comma - (eq + 1));
     340           26 :               iter = comma + 1;
     341           26 :               last_separator = ",";
     342              :             }
     343              :           else
     344              :             {
     345           44 :               value = std::string (eq + 1);
     346           44 :               iter = nullptr;
     347              :             }
     348          210 :           result.m_kvs.push_back ({std::move (key), std::move (value)});
     349           70 :         }
     350              :     }
     351              :   else
     352           22 :     result.m_scheme_name = unparsed_spec;
     353           66 :   return std::make_unique<scheme_name_and_params> (std::move (result));
     354           82 : }
     355              : 
     356              : std::unique_ptr<sink>
     357           54 : context::parse_and_make_sink (diagnostics::context &dc)
     358              : {
     359           54 :   auto parsed_arg = parse (*this);
     360           54 :   if (!parsed_arg)
     361            0 :     return nullptr;
     362              : 
     363           54 :   output_factory factory (dc);
     364           54 :   return factory.make_sink (*this, dc, *parsed_arg);
     365           54 : }
     366              : 
     367              : /* class scheme_handler.  */
     368              : 
     369              : /* class output_factory.  */
     370              : 
     371           54 : output_factory::output_factory (diagnostics::context &dc)
     372              : {
     373           54 :   m_scheme_handlers.push_back (std::make_unique<text_scheme_handler> (dc));
     374           54 :   m_scheme_handlers.push_back (std::make_unique<sarif_scheme_handler> ());
     375           54 :   m_scheme_handlers.push_back (std::make_unique<html_scheme_handler> ());
     376           54 : }
     377              : 
     378              : scheme_handler *
     379           54 : output_factory::get_scheme_handler (const std::string &scheme_name)
     380              : {
     381          106 :   for (auto &iter : m_scheme_handlers)
     382          106 :     if (iter->get_scheme_name () == scheme_name)
     383           54 :       return iter.get ();
     384              :   return nullptr;
     385              : }
     386              : 
     387              : std::unique_ptr<sink>
     388           54 : output_factory::make_sink (const context &ctxt,
     389              :                            diagnostics::context &dc,
     390              :                            const scheme_name_and_params &scheme_and_kvs)
     391              : {
     392           54 :   auto scheme_handler = get_scheme_handler (scheme_and_kvs.m_scheme_name);
     393           54 :   if (!scheme_handler)
     394              :     {
     395            0 :       auto_vec<const char *> strings;
     396            0 :       for (auto &iter : m_scheme_handlers)
     397            0 :         strings.safe_push (iter->get_scheme_name ().c_str ());
     398            0 :       pp_markup::comma_separated_quoted_strings e (strings);
     399            0 :       ctxt.report_error ("%<%s%s%>:"
     400              :                          " unrecognized format %qs; known formats: %e",
     401              :                          ctxt.get_option_name (), ctxt.get_unparsed_spec (),
     402              :                          scheme_and_kvs.m_scheme_name.c_str (), &e);
     403            0 :       return nullptr;
     404            0 :     }
     405              : 
     406              :   /* Parse key/value pairs.  */
     407          108 :   for (auto& iter : scheme_and_kvs.m_kvs)
     408              :     {
     409           54 :       const std::string &key = iter.first;
     410           54 :       const std::string &value = iter.second;
     411           54 :       if (!ctxt.handle_kv (key, value, *scheme_handler))
     412            0 :         return nullptr;
     413              :     }
     414              : 
     415           54 :   return scheme_handler->make_sink (ctxt, dc);
     416              : }
     417              : 
     418              : bool
     419           54 : context::handle_kv (const std::string &key,
     420              :                     const std::string &value,
     421              :                     scheme_handler &scheme) const
     422              : {
     423           54 :   auto result = scheme.maybe_handle_kv (*this, key, value);
     424           54 :   switch (result)
     425              :     {
     426            0 :     default: gcc_unreachable ();
     427              :     case key_handler::result::ok:
     428              :       return true;
     429              :     case key_handler::result::malformed_value:
     430              :       return false;
     431           10 :     case key_handler::result::unrecognized:
     432              :       /* Key recognized by the scheme; try the client keys.  */
     433           10 :       if (m_client_keys)
     434              :         {
     435           10 :           result = m_client_keys->maybe_handle_kv (*this, key, value);
     436           10 :           switch (result)
     437              :             {
     438            0 :             default: gcc_unreachable ();
     439              :             case key_handler::result::ok:
     440              :               return true;
     441              :             case key_handler::result::malformed_value:
     442              :               return false;
     443              :             case key_handler::result::unrecognized:
     444              :               break;
     445              :             }
     446              :         }
     447            0 :       report_unknown_key (key, scheme);
     448            0 :       return false;
     449              :     }
     450              : }
     451              : 
     452              : /* class text_scheme_handler : public scheme_handler.  */
     453              : 
     454              : std::unique_ptr<sink>
     455           16 : text_scheme_handler::make_sink (const context &,
     456              :                                 diagnostics::context &dc)
     457              : {
     458           16 :   auto sink = std::make_unique<diagnostics::text_sink> (dc);
     459           16 :   sink->set_show_nesting (m_show_nesting);
     460           16 :   sink->set_show_locations_in_nesting (m_show_locations_in_nesting);
     461           16 :   sink->set_show_nesting_levels (m_show_levels);
     462           16 :   pp_show_color (sink->get_printer ()) = m_show_color;
     463           16 :   return sink;
     464           16 : }
     465              : 
     466              : enum key_handler::result
     467           30 : text_scheme_handler::maybe_handle_kv (const context &ctxt,
     468              :                                       const std::string &key,
     469              :                                       const std::string &value)
     470              : {
     471           30 :   if (key == "color")
     472            0 :     return parse_bool_value (ctxt, key, value, m_show_color);
     473           30 :   if (key == "show-nesting")
     474           12 :     return parse_bool_value (ctxt, key, value, m_show_nesting);
     475           18 :   if (key == "show-nesting-locations")
     476            9 :     return parse_bool_value (ctxt, key, value,
     477            9 :                              m_show_locations_in_nesting);
     478            9 :   if (key == "show-nesting-levels")
     479            1 :     return parse_bool_value (ctxt, key, value, m_show_levels);
     480              : 
     481              :   return result::unrecognized;
     482              : }
     483              : 
     484              : void
     485            0 : text_scheme_handler::get_keys (auto_vec<const char *> &out) const
     486              : {
     487            0 :   out.safe_push ("color");
     488            0 :   out.safe_push ("show-nesting");
     489            0 :   out.safe_push ("show-nesting-locations");
     490            0 :   out.safe_push ("show-nesting-levels");
     491            0 : }
     492              : 
     493              : /* class sarif_scheme_handler : public scheme_handler.  */
     494              : 
     495              : std::unique_ptr<sink>
     496           24 : sarif_scheme_handler::
     497              : make_sink (const context &ctxt,
     498              :            diagnostics::context &dc)
     499              : {
     500           24 :   output_file output_file_;
     501           24 :   if (m_filename.get ())
     502            2 :     output_file_ = ctxt.open_output_file (std::move (m_filename));
     503              :   else
     504              :     // Default filename
     505              :     {
     506           22 :       const char *basename = ctxt.get_base_filename ();
     507           22 :       if (!basename)
     508              :         {
     509            0 :           ctxt.report_missing_key ("file",
     510              :                                    get_scheme_name (),
     511              :                                    "FILENAME");
     512            0 :           return nullptr;
     513              :         }
     514           22 :       output_file_
     515           44 :         = open_sarif_output_file (dc,
     516              :                                   ctxt.get_affected_location_mgr (),
     517              :                                   basename,
     518           22 :                                   m_serialization_kind);
     519              :     }
     520           24 :   if (!output_file_)
     521            0 :     return nullptr;
     522              : 
     523           24 :   auto serialization_obj
     524           24 :     = make_sarif_serialization_object (m_serialization_kind);
     525              : 
     526           24 :   auto sink = make_sarif_sink (dc,
     527           24 :                                *ctxt.get_affected_location_mgr (),
     528              :                                std::move (serialization_obj),
     529           24 :                                m_generation_opts,
     530           24 :                                std::move (output_file_));
     531              : 
     532           24 :   return sink;
     533           24 : }
     534              : 
     535              : enum key_handler::result
     536            8 : sarif_scheme_handler::maybe_handle_kv (const context &ctxt,
     537              :                                        const std::string &key,
     538              :                                        const std::string &value)
     539              : {
     540            8 :   if (key == "file")
     541              :     {
     542            2 :       m_filename = label_text::take (xstrdup (value.c_str ()));
     543            2 :       return result::ok;
     544              :     }
     545            6 :   if (key == "serialization")
     546              :     {
     547            0 :       static const std::array<std::pair<const char *, enum sarif_serialization_kind>,
     548              :                               (size_t)sarif_serialization_kind::num_values> value_names
     549              :         {{{"json", sarif_serialization_kind::json}}};
     550            0 :       return parse_enum_value<enum sarif_serialization_kind>
     551            0 :         (ctxt,
     552              :          key, value,
     553              :          value_names,
     554            0 :          m_serialization_kind);
     555              :     }
     556            6 :   if (key == "version")
     557              :     {
     558            3 :       static const std::array<std::pair<const char *, enum sarif_version>,
     559              :                               (size_t)sarif_version::num_versions> value_names
     560              :         {{{"2.1", sarif_version::v2_1_0},
     561              :           {"2.2-prerelease", sarif_version::v2_2_prerelease_2024_08_08}}};
     562            3 :       return parse_enum_value<enum sarif_version>
     563            3 :         (ctxt,
     564              :          key, value,
     565              :          value_names,
     566            3 :          m_generation_opts.m_version);
     567              :     }
     568            3 :   if (key == "state-graphs")
     569            2 :     return parse_bool_value (ctxt, key, value,
     570            2 :                              m_generation_opts.m_state_graph);
     571              : 
     572              :   return result::unrecognized;
     573              : }
     574              : 
     575              : void
     576            0 : sarif_scheme_handler::get_keys (auto_vec<const char *> &out) const
     577              : {
     578            0 :   out.safe_push ("file");
     579            0 :   out.safe_push ("serialization");
     580            0 :   out.safe_push ("state-graphs");
     581            0 :   out.safe_push ("version");
     582            0 : }
     583              : 
     584              : std::unique_ptr<sarif_serialization_format>
     585           24 : sarif_scheme_handler::
     586              : make_sarif_serialization_object (enum sarif_serialization_kind kind)
     587              : {
     588           24 :   switch (kind)
     589              :     {
     590            0 :     default:
     591            0 :       gcc_unreachable ();
     592           24 :     case sarif_serialization_kind::json:
     593           24 :       return std::make_unique<sarif_serialization_format_json> (true);
     594              :       break;
     595              :     }
     596              : }
     597              : 
     598              : /* class html_scheme_handler : public scheme_handler.  */
     599              : 
     600              : std::unique_ptr<sink>
     601           14 : html_scheme_handler::
     602              : make_sink (const context &ctxt,
     603              :            diagnostics::context &dc)
     604              : {
     605           14 :   output_file output_file_;
     606           14 :   if (m_filename.get ())
     607            0 :     output_file_ = ctxt.open_output_file (std::move (m_filename));
     608              :   else
     609              :     // Default filename
     610              :     {
     611           14 :       const char *basename = ctxt.get_base_filename ();
     612           14 :       if (!basename)
     613              :         {
     614            0 :           ctxt.report_missing_key ("file",
     615              :                                    get_scheme_name (),
     616              :                                    "FILENAME");
     617            0 :           return nullptr;
     618              :         }
     619           14 :       output_file_
     620              :         = open_html_output_file
     621           28 :             (dc,
     622              :              ctxt.get_affected_location_mgr (),
     623           14 :              basename);
     624              :     }
     625           14 :   if (!output_file_)
     626            0 :     return nullptr;
     627              : 
     628           28 :   auto sink = make_html_sink (dc,
     629           14 :                               *ctxt.get_affected_location_mgr (),
     630           14 :                               m_html_gen_opts,
     631           14 :                               std::move (output_file_));
     632           14 :   return sink;
     633           14 : }
     634              : 
     635              : enum key_handler::result
     636           16 : html_scheme_handler::maybe_handle_kv (const context &ctxt,
     637              :                                       const std::string &key,
     638              :                                       const std::string &value)
     639              : {
     640           16 :   if (key == "css")
     641            0 :     return parse_bool_value (ctxt, key, value, m_html_gen_opts.m_css);
     642           16 :   if (key == "file")
     643              :     {
     644            0 :       m_filename = label_text::take (xstrdup (value.c_str ()));
     645            0 :       return result::ok;
     646              :     }
     647           16 :   if (key == "javascript")
     648           14 :     return parse_bool_value (ctxt, key, value,
     649           14 :                              m_html_gen_opts.m_javascript);
     650            2 :   if (key == "show-state-diagrams")
     651            1 :     return parse_bool_value (ctxt, key, value,
     652            1 :                              m_html_gen_opts.m_show_state_diagrams);
     653            1 :   if (key == "show-graph-dot-src")
     654            0 :     return parse_bool_value (ctxt, key, value,
     655            0 :                              m_html_gen_opts.m_show_graph_dot_src);
     656            1 :   if (key == "show-graph-sarif")
     657            0 :     return parse_bool_value (ctxt, key, value,
     658            0 :                              m_html_gen_opts.m_show_graph_sarif);
     659              :   return result::unrecognized;
     660              : }
     661              : 
     662              : void
     663            0 : html_scheme_handler::get_keys (auto_vec<const char *> &out) const
     664              : {
     665            0 :   out.safe_push ("css");
     666            0 :   out.safe_push ("file");
     667            0 :   out.safe_push ("javascript");
     668            0 :   out.safe_push ("show-state-diagrams");
     669            0 :   out.safe_push ("show-graph-dot-src");
     670            0 :   out.safe_push ("show-graph-sarif");
     671            0 : }
     672              : 
     673              : } // namespace output_spec
     674              : 
     675              : #if CHECKING_P
     676              : 
     677              : namespace selftest {
     678              : 
     679              : using auto_fix_quotes = ::selftest::auto_fix_quotes;
     680              : 
     681              : /* RAII class to temporarily override "progname" to the
     682              :    string "PROGNAME".  */
     683              : 
     684              : class auto_fix_progname
     685              : {
     686              : public:
     687            4 :   auto_fix_progname ()
     688            4 :   {
     689            4 :     m_old_progname = progname;
     690            4 :     progname = "PROGNAME";
     691              :   }
     692              : 
     693            4 :   ~auto_fix_progname ()
     694              :   {
     695            4 :     progname = m_old_progname;
     696            4 :   }
     697              : 
     698              : private:
     699              :   const char *m_old_progname;
     700              : };
     701              : 
     702           64 : struct parser_test
     703              : {
     704           32 :   class test_spec_context : public diagnostics::output_spec::dc_spec_context
     705              :   {
     706              :   public:
     707           32 :     test_spec_context (const char *option_name,
     708              :                        const char *unparsed_spec,
     709              :                        diagnostics::output_spec::key_handler *client_keys,
     710              :                        line_maps *location_mgr,
     711              :                        diagnostics::context &dc,
     712              :                        location_t loc)
     713              :     : dc_spec_context (option_name,
     714              :                        unparsed_spec,
     715              :                        client_keys,
     716              :                        location_mgr,
     717              :                        dc,
     718              :                        location_mgr,
     719           32 :                        loc)
     720              :     {
     721              :     }
     722              : 
     723              :     const char *
     724            0 :     get_base_filename () const final override
     725              :     {
     726            0 :       return "BASE_FILENAME";
     727              :     }
     728              :   };
     729              : 
     730           32 :   parser_test (const char *unparsed_spec,
     731              :                diagnostics::output_spec::key_handler *client_keys = nullptr)
     732           32 :   : m_dc (),
     733           32 :     m_ctxt ("-fOPTION=",
     734              :             unparsed_spec,
     735              :             client_keys,
     736              :             line_table,
     737              :             m_dc,
     738              :             UNKNOWN_LOCATION),
     739           32 :     m_fmt (m_dc.get_sink (0))
     740              :   {
     741           32 :     pp_buffer (m_fmt.get_printer ())->m_flush_p = false;
     742           32 :   }
     743              : 
     744              :   std::unique_ptr<diagnostics::output_spec::scheme_name_and_params>
     745           28 :   parse ()
     746              :   {
     747           28 :     return diagnostics::output_spec::parse (m_ctxt);
     748              :   }
     749              : 
     750              :   std::unique_ptr<diagnostics::sink>
     751            4 :   parse_and_make_sink ()
     752              :   {
     753            4 :     return m_ctxt.parse_and_make_sink (m_dc);
     754              :   }
     755              : 
     756           28 :   bool execution_failed_p () const
     757              :   {
     758           28 :     return m_dc.execution_failed_p ();
     759              :   }
     760              : 
     761              :   const char *
     762           16 :   get_diagnostic_text () const
     763              :   {
     764           16 :     return pp_formatted_text (m_fmt.get_printer ());
     765              :   }
     766              : 
     767              : private:
     768              :   diagnostics::selftest::test_context m_dc;
     769              :   test_spec_context m_ctxt;
     770              :   diagnostics::sink &m_fmt;
     771              : };
     772              : 
     773              : /* Selftests.  */
     774              : 
     775              : static void
     776            4 : test_output_arg_parsing ()
     777              : {
     778              :   /* Minimal correct example.  */
     779            4 :   {
     780            4 :     parser_test pt ("foo");
     781            4 :     auto result = pt.parse ();
     782            4 :     ASSERT_EQ (result->m_scheme_name, "foo");
     783            4 :     ASSERT_EQ (result->m_kvs.size (), 0);
     784            4 :     ASSERT_FALSE (pt.execution_failed_p ());
     785            4 :   }
     786              : 
     787              :   /* Stray trailing colon with no key/value pairs.  */
     788            4 :   {
     789            4 :     parser_test pt ("foo:");
     790            4 :     auto result = pt.parse ();
     791            4 :     ASSERT_EQ (result, nullptr);
     792            4 :     ASSERT_TRUE (pt.execution_failed_p ());
     793            4 :     ASSERT_STREQ (pt.get_diagnostic_text (),
     794              :                   "PROGNAME: error: `-fOPTION=foo:':"
     795              :                   " expected KEY=VALUE-style parameter for format `foo'"
     796              :                   " after `:';"
     797              :                   " got `'\n");
     798            4 :   }
     799              : 
     800              :   /* No key before '='.  */
     801            4 :   {
     802            4 :     parser_test pt ("foo:=");
     803            4 :     auto result = pt.parse ();
     804            4 :     ASSERT_EQ (result, nullptr);
     805            4 :     ASSERT_TRUE (pt.execution_failed_p ());
     806            4 :     ASSERT_STREQ (pt.get_diagnostic_text (),
     807              :                   "PROGNAME: error: `-fOPTION=foo:=':"
     808              :                   " expected KEY=VALUE-style parameter for format `foo'"
     809              :                   " after `:';"
     810              :                   " got `='\n");
     811            4 :   }
     812              : 
     813              :   /* No value for key.  */
     814            4 :   {
     815            4 :     parser_test pt ("foo:key,");
     816            4 :     auto result = pt.parse ();
     817            4 :     ASSERT_EQ (result, nullptr);
     818            4 :     ASSERT_TRUE (pt.execution_failed_p ());
     819            4 :     ASSERT_STREQ (pt.get_diagnostic_text (),
     820              :                   "PROGNAME: error: `-fOPTION=foo:key,':"
     821              :                   " expected KEY=VALUE-style parameter for format `foo'"
     822              :                   " after `:';"
     823              :                   " got `key,'\n");
     824            4 :   }
     825              : 
     826              :   /* Correct example, with one key/value pair.  */
     827            4 :   {
     828            4 :     parser_test pt ("foo:key=value");
     829            4 :     auto result = pt.parse ();
     830            4 :     ASSERT_EQ (result->m_scheme_name, "foo");
     831            4 :     ASSERT_EQ (result->m_kvs.size (), 1);
     832            4 :     ASSERT_EQ (result->m_kvs[0].first, "key");
     833            4 :     ASSERT_EQ (result->m_kvs[0].second, "value");
     834            4 :     ASSERT_FALSE (pt.execution_failed_p ());
     835            4 :   }
     836              : 
     837              :   /* Stray trailing comma.  */
     838            4 :   {
     839            4 :     parser_test pt ("foo:key=value,");
     840            4 :     auto result = pt.parse ();
     841            4 :     ASSERT_EQ (result, nullptr);
     842            4 :     ASSERT_TRUE (pt.execution_failed_p ());
     843            4 :     ASSERT_STREQ (pt.get_diagnostic_text (),
     844              :                   "PROGNAME: error: `-fOPTION=foo:key=value,':"
     845              :                   " expected KEY=VALUE-style parameter for format `foo'"
     846              :                   " after `,';"
     847              :                   " got `'\n");
     848            4 :   }
     849              : 
     850              :   /* Correct example, with two key/value pairs.  */
     851            4 :   {
     852            4 :     parser_test pt ("foo:color=red,shape=circle");
     853            4 :     auto result = pt.parse ();
     854            4 :     ASSERT_EQ (result->m_scheme_name, "foo");
     855            4 :     ASSERT_EQ (result->m_kvs.size (), 2);
     856            4 :     ASSERT_EQ (result->m_kvs[0].first, "color");
     857            4 :     ASSERT_EQ (result->m_kvs[0].second, "red");
     858            4 :     ASSERT_EQ (result->m_kvs[1].first, "shape");
     859            4 :     ASSERT_EQ (result->m_kvs[1].second, "circle");
     860            4 :     ASSERT_FALSE (pt.execution_failed_p ());
     861            4 :   }
     862            4 : }
     863              : 
     864              : class test_key_handler : public diagnostics::output_spec::key_handler
     865              : {
     866              : public:
     867            4 :   test_key_handler ()
     868            4 :   : m_verbose (false),
     869            4 :     m_strict (false)
     870              :   {
     871              :   }
     872              : 
     873              :   enum result
     874            8 :   maybe_handle_kv (const diagnostics::output_spec::context &ctxt,
     875              :                    const std::string &key,
     876              :                    const std::string &value) final override
     877              :   {
     878            8 :     if (key == "verbose")
     879            4 :       return parse_bool_value (ctxt, key, value, m_verbose);
     880            4 :     if (key == "strict")
     881            4 :       return parse_bool_value (ctxt, key, value, m_strict);
     882              :     return result::unrecognized;
     883              :   }
     884              : 
     885              :   void
     886            0 :   get_keys (auto_vec<const char *> &out_known_keys) const final override
     887              :   {
     888            0 :     out_known_keys.safe_push ("verbose");
     889            0 :     out_known_keys.safe_push ("strict");
     890            0 :   }
     891              : 
     892              :   bool m_verbose;
     893              :   bool m_strict;
     894              : };
     895              : 
     896              : static void
     897            4 : test_client_arg_parsing ()
     898              : {
     899            4 :   test_key_handler client_keys;
     900            4 :   parser_test pt ("text:verbose=yes,strict=no", &client_keys);
     901            4 :   auto result = pt.parse_and_make_sink ();
     902            4 :   ASSERT_TRUE (result.get ());
     903            4 :   ASSERT_TRUE (client_keys.m_verbose);
     904            4 :   ASSERT_FALSE (client_keys.m_strict);
     905            4 : }
     906              : 
     907              : /* Run all of the selftests within this file.  */
     908              : 
     909              : void
     910            4 : output_spec_cc_tests ()
     911              : {
     912            4 :   auto_fix_quotes fix_quotes;
     913            4 :   auto_fix_progname fix_progname;
     914              : 
     915            4 :   test_output_arg_parsing ();
     916            4 :   test_client_arg_parsing ();
     917            4 : }
     918              : 
     919              : } // namespace diagnostics::selftest
     920              : 
     921              : #endif /* #if CHECKING_P */
     922              : 
     923              : } // namespace diagnostics
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.