LCOV - code coverage report
Current view: top level - gcc/diagnostics - output-spec.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 74.0 % 369 273
Test Date: 2025-10-18 14:39:06 Functions: 75.8 % 33 25
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* Support for the DSL of -fdiagnostics-add-output= and
       2                 :             :    -fdiagnostics-set-output=.
       3                 :             :    Copyright (C) 2024-2025 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                 :         122 : 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                 :          82 : 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                 :          46 : key_handler::parse_bool_value (const context &ctxt,
      91                 :             :                                const std::string &key,
      92                 :             :                                const std::string &value,
      93                 :             :                                bool &out) const
      94                 :             : {
      95                 :          46 :   if (value == "yes")
      96                 :             :     {
      97                 :          20 :       out = true;
      98                 :          20 :       return result::ok;
      99                 :             :     }
     100                 :          26 :   else if (value == "no")
     101                 :             :     {
     102                 :          26 :       out = false;
     103                 :          26 :       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                 :          41 :   text_scheme_handler (diagnostics::context &dc)
     153                 :          41 :   : scheme_handler ("text"),
     154                 :          41 :     m_show_color (pp_show_color (dc.get_reference_printer ())),
     155                 :          41 :     m_show_nesting (true),
     156                 :          41 :     m_show_locations_in_nesting (true),
     157                 :          82 :     m_show_levels (false)
     158                 :             :   {
     159                 :          41 :   }
     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                 :          41 :   sarif_scheme_handler ()
     184                 :          41 :   : scheme_handler ("sarif"),
     185                 :          82 :     m_serialization_kind (sarif_serialization_kind::json)
     186                 :             :   {
     187                 :          41 :   }
     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                 :          41 :   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                 :          69 : parse (const context &ctxt)
     307                 :             : {
     308                 :          69 :   scheme_name_and_params result;
     309                 :          69 :   const char *const unparsed_spec = ctxt.get_unparsed_spec ();
     310                 :          69 :   if (const char *const colon = strchr (unparsed_spec, ':'))
     311                 :             :     {
     312                 :          58 :       result.m_scheme_name = std::string (unparsed_spec, colon - unparsed_spec);
     313                 :             :       /* Expect zero of more of KEY=VALUE,KEY=VALUE, etc  .*/
     314                 :          58 :       const char *iter = colon + 1;
     315                 :          58 :       const char *last_separator = ":";
     316                 :         125 :       while (iter)
     317                 :             :         {
     318                 :             :           /* Look for a non-empty key string followed by '='.  */
     319                 :          83 :           const char *eq = strchr (iter, '=');
     320                 :          83 :           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                 :          67 :           std::string key = std::string (iter, eq - iter);
     335                 :          67 :           std::string value;
     336                 :          67 :           const char *comma = strchr (iter, ',');
     337                 :          67 :           if (comma)
     338                 :             :             {
     339                 :          25 :               value = std::string (eq + 1, comma - (eq + 1));
     340                 :          25 :               iter = comma + 1;
     341                 :          25 :               last_separator = ",";
     342                 :             :             }
     343                 :             :           else
     344                 :             :             {
     345                 :          42 :               value = std::string (eq + 1);
     346                 :          42 :               iter = nullptr;
     347                 :             :             }
     348                 :         201 :           result.m_kvs.push_back ({std::move (key), std::move (value)});
     349                 :          67 :         }
     350                 :             :     }
     351                 :             :   else
     352                 :          11 :     result.m_scheme_name = unparsed_spec;
     353                 :          53 :   return std::make_unique<scheme_name_and_params> (std::move (result));
     354                 :          69 : }
     355                 :             : 
     356                 :             : std::unique_ptr<sink>
     357                 :          41 : context::parse_and_make_sink (diagnostics::context &dc)
     358                 :             : {
     359                 :          41 :   auto parsed_arg = parse (*this);
     360                 :          41 :   if (!parsed_arg)
     361                 :           0 :     return nullptr;
     362                 :             : 
     363                 :          41 :   output_factory factory (dc);
     364                 :          41 :   return factory.make_sink (*this, dc, *parsed_arg);
     365                 :          41 : }
     366                 :             : 
     367                 :             : /* class scheme_handler.  */
     368                 :             : 
     369                 :             : /* class output_factory.  */
     370                 :             : 
     371                 :          41 : output_factory::output_factory (diagnostics::context &dc)
     372                 :             : {
     373                 :          41 :   m_scheme_handlers.push_back (std::make_unique<text_scheme_handler> (dc));
     374                 :          41 :   m_scheme_handlers.push_back (std::make_unique<sarif_scheme_handler> ());
     375                 :          41 :   m_scheme_handlers.push_back (std::make_unique<html_scheme_handler> ());
     376                 :          41 : }
     377                 :             : 
     378                 :             : scheme_handler *
     379                 :          41 : output_factory::get_scheme_handler (const std::string &scheme_name)
     380                 :             : {
     381                 :          79 :   for (auto &iter : m_scheme_handlers)
     382                 :          79 :     if (iter->get_scheme_name () == scheme_name)
     383                 :          41 :       return iter.get ();
     384                 :             :   return nullptr;
     385                 :             : }
     386                 :             : 
     387                 :             : std::unique_ptr<sink>
     388                 :          41 : output_factory::make_sink (const context &ctxt,
     389                 :             :                            diagnostics::context &dc,
     390                 :             :                            const scheme_name_and_params &scheme_and_kvs)
     391                 :             : {
     392                 :          41 :   auto scheme_handler = get_scheme_handler (scheme_and_kvs.m_scheme_name);
     393                 :          41 :   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                 :          92 :   for (auto& iter : scheme_and_kvs.m_kvs)
     408                 :             :     {
     409                 :          51 :       const std::string &key = iter.first;
     410                 :          51 :       const std::string &value = iter.second;
     411                 :          51 :       if (!ctxt.handle_kv (key, value, *scheme_handler))
     412                 :           0 :         return nullptr;
     413                 :             :     }
     414                 :             : 
     415                 :          41 :   return scheme_handler->make_sink (ctxt, dc);
     416                 :             : }
     417                 :             : 
     418                 :             : bool
     419                 :          51 : context::handle_kv (const std::string &key,
     420                 :             :                     const std::string &value,
     421                 :             :                     scheme_handler &scheme) const
     422                 :             : {
     423                 :          51 :   auto result = scheme.maybe_handle_kv (*this, key, value);
     424                 :          51 :   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                 :           8 :     case key_handler::result::unrecognized:
     432                 :             :       /* Key recognized by the scheme; try the client keys.  */
     433                 :           8 :       if (m_client_keys)
     434                 :             :         {
     435                 :           8 :           result = m_client_keys->maybe_handle_kv (*this, key, value);
     436                 :           8 :           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                 :          12 : sarif_scheme_handler::
     497                 :             : make_sink (const context &ctxt,
     498                 :             :            diagnostics::context &dc)
     499                 :             : {
     500                 :          12 :   output_file output_file_;
     501                 :          12 :   if (m_filename.get ())
     502                 :           2 :     output_file_ = ctxt.open_output_file (std::move (m_filename));
     503                 :             :   else
     504                 :             :     // Default filename
     505                 :             :     {
     506                 :          10 :       const char *basename = ctxt.get_base_filename ();
     507                 :          10 :       if (!basename)
     508                 :             :         {
     509                 :           0 :           ctxt.report_missing_key ("file",
     510                 :             :                                    get_scheme_name (),
     511                 :             :                                    "FILENAME");
     512                 :           0 :           return nullptr;
     513                 :             :         }
     514                 :          10 :       output_file_
     515                 :          20 :         = open_sarif_output_file (dc,
     516                 :             :                                   ctxt.get_affected_location_mgr (),
     517                 :             :                                   basename,
     518                 :          10 :                                   m_serialization_kind);
     519                 :             :     }
     520                 :          12 :   if (!output_file_)
     521                 :           0 :     return nullptr;
     522                 :             : 
     523                 :          12 :   auto serialization_obj
     524                 :          12 :     = make_sarif_serialization_object (m_serialization_kind);
     525                 :             : 
     526                 :          12 :   auto sink = make_sarif_sink (dc,
     527                 :          12 :                                *ctxt.get_affected_location_mgr (),
     528                 :             :                                std::move (serialization_obj),
     529                 :          12 :                                m_generation_opts,
     530                 :          12 :                                std::move (output_file_));
     531                 :             : 
     532                 :          12 :   return sink;
     533                 :          12 : }
     534                 :             : 
     535                 :             : enum key_handler::result
     536                 :           7 : sarif_scheme_handler::maybe_handle_kv (const context &ctxt,
     537                 :             :                                        const std::string &key,
     538                 :             :                                        const std::string &value)
     539                 :             : {
     540                 :           7 :   if (key == "file")
     541                 :             :     {
     542                 :           2 :       m_filename = label_text::take (xstrdup (value.c_str ()));
     543                 :           2 :       return result::ok;
     544                 :             :     }
     545                 :           5 :   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                 :           5 :   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                 :           2 :   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                 :          12 : sarif_scheme_handler::
     586                 :             : make_sarif_serialization_object (enum sarif_serialization_kind kind)
     587                 :             : {
     588                 :          12 :   switch (kind)
     589                 :             :     {
     590                 :           0 :     default:
     591                 :           0 :       gcc_unreachable ();
     592                 :          12 :     case sarif_serialization_kind::json:
     593                 :          12 :       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                 :          13 : html_scheme_handler::
     602                 :             : make_sink (const context &ctxt,
     603                 :             :            diagnostics::context &dc)
     604                 :             : {
     605                 :          13 :   output_file output_file_;
     606                 :          13 :   if (m_filename.get ())
     607                 :           0 :     output_file_ = ctxt.open_output_file (std::move (m_filename));
     608                 :             :   else
     609                 :             :     // Default filename
     610                 :             :     {
     611                 :          13 :       const char *basename = ctxt.get_base_filename ();
     612                 :          13 :       if (!basename)
     613                 :             :         {
     614                 :           0 :           ctxt.report_missing_key ("file",
     615                 :             :                                    get_scheme_name (),
     616                 :             :                                    "FILENAME");
     617                 :           0 :           return nullptr;
     618                 :             :         }
     619                 :          13 :       output_file_
     620                 :             :         = open_html_output_file
     621                 :          26 :             (dc,
     622                 :             :              ctxt.get_affected_location_mgr (),
     623                 :          13 :              basename);
     624                 :             :     }
     625                 :          13 :   if (!output_file_)
     626                 :           0 :     return nullptr;
     627                 :             : 
     628                 :          26 :   auto sink = make_html_sink (dc,
     629                 :          13 :                               *ctxt.get_affected_location_mgr (),
     630                 :          13 :                               m_html_gen_opts,
     631                 :          13 :                               std::move (output_file_));
     632                 :          13 :   return sink;
     633                 :          13 : }
     634                 :             : 
     635                 :             : enum key_handler::result
     636                 :          14 : html_scheme_handler::maybe_handle_kv (const context &ctxt,
     637                 :             :                                       const std::string &key,
     638                 :             :                                       const std::string &value)
     639                 :             : {
     640                 :          14 :   if (key == "css")
     641                 :           0 :     return parse_bool_value (ctxt, key, value, m_html_gen_opts.m_css);
     642                 :          14 :   if (key == "file")
     643                 :             :     {
     644                 :           0 :       m_filename = label_text::take (xstrdup (value.c_str ()));
     645                 :           0 :       return result::ok;
     646                 :             :     }
     647                 :          14 :   if (key == "javascript")
     648                 :          13 :     return parse_bool_value (ctxt, key, value,
     649                 :          13 :                              m_html_gen_opts.m_javascript);
     650                 :           1 :   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                 :           0 :   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                 :           0 :   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.1-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.