LCOV - code coverage report
Current view: top level - gcc/diagnostics - output-spec.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 72.6 % 347 252
Test Date: 2025-09-20 13:40:47 Functions: 82.6 % 23 19
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                 :             : 
      47                 :             : namespace diagnostics {
      48                 :             : namespace output_spec {
      49                 :             : 
      50                 :             : /* Decls.  */
      51                 :             : 
      52                 :         114 : struct scheme_name_and_params
      53                 :             : {
      54                 :             :   std::string m_scheme_name;
      55                 :             :   std::vector<std::pair<std::string, std::string>> m_kvs;
      56                 :             : };
      57                 :             : 
      58                 :             : /* Class for parsing the arguments of -fdiagnostics-add-output= and
      59                 :             :    -fdiagnostics-set-output=, and making sink
      60                 :             :    instances (or issuing errors).  */
      61                 :             : 
      62                 :          74 : class output_factory
      63                 :             : {
      64                 :             : public:
      65                 :             :   class scheme_handler
      66                 :             :   {
      67                 :             :   public:
      68                 :         111 :     scheme_handler (std::string scheme_name)
      69                 :         111 :     : m_scheme_name (std::move (scheme_name))
      70                 :             :     {}
      71                 :             :     virtual ~scheme_handler () {}
      72                 :             : 
      73                 :          75 :     const std::string &get_scheme_name () const { return m_scheme_name; }
      74                 :             : 
      75                 :             :     virtual std::unique_ptr<sink>
      76                 :             :     make_sink (const context &ctxt,
      77                 :             :                diagnostics::context &dc,
      78                 :             :                const char *unparsed_arg,
      79                 :             :                const scheme_name_and_params &parsed_arg) const = 0;
      80                 :             : 
      81                 :             :   protected:
      82                 :             :     bool
      83                 :          38 :     parse_bool_value (const context &ctxt,
      84                 :             :                       const char *unparsed_arg,
      85                 :             :                       const std::string &key,
      86                 :             :                       const std::string &value,
      87                 :             :                       bool &out) const
      88                 :             :     {
      89                 :          38 :       if (value == "yes")
      90                 :             :         {
      91                 :          16 :           out = true;
      92                 :          16 :           return true;
      93                 :             :         }
      94                 :          22 :       else if (value == "no")
      95                 :             :         {
      96                 :          22 :           out = false;
      97                 :          22 :           return true;
      98                 :             :         }
      99                 :             :       else
     100                 :             :         {
     101                 :           0 :           ctxt.report_error
     102                 :           0 :             ("%<%s%s%>:"
     103                 :             :              " unexpected value %qs for key %qs; expected %qs or %qs",
     104                 :             :              ctxt.get_option_name (), unparsed_arg,
     105                 :             :              value.c_str (),
     106                 :             :              key.c_str (),
     107                 :             :              "yes", "no");
     108                 :             : 
     109                 :           0 :           return false;
     110                 :             :         }
     111                 :             :     }
     112                 :             :     template <typename EnumType, size_t NumValues>
     113                 :             :     bool
     114                 :           3 :     parse_enum_value (const context &ctxt,
     115                 :             :                       const char *unparsed_arg,
     116                 :             :                       const std::string &key,
     117                 :             :                       const std::string &value,
     118                 :             :                       const std::array<std::pair<const char *, EnumType>, NumValues> &value_names,
     119                 :             :                       EnumType &out) const
     120                 :             :     {
     121                 :           5 :       for (auto &iter : value_names)
     122                 :           5 :         if (value == iter.first)
     123                 :             :           {
     124                 :           3 :             out = iter.second;
     125                 :           3 :             return true;
     126                 :             :           }
     127                 :             : 
     128                 :           0 :       auto_vec<const char *> known_values;
     129                 :           0 :       for (auto iter : value_names)
     130                 :           0 :         known_values.safe_push (iter.first);
     131                 :           0 :       pp_markup::comma_separated_quoted_strings e (known_values);
     132                 :             :       ctxt.report_error
     133                 :           0 :         ("%<%s%s%>:"
     134                 :             :          " unexpected value %qs for key %qs; known values: %e",
     135                 :             :          ctxt.get_option_name (), unparsed_arg,
     136                 :             :          value.c_str (),
     137                 :             :          key.c_str (),
     138                 :             :          &e);
     139                 :           0 :       return false;
     140                 :           0 :     }
     141                 :             : 
     142                 :             :   private:
     143                 :             :     const std::string m_scheme_name;
     144                 :             :   };
     145                 :             : 
     146                 :             :   output_factory ();
     147                 :             : 
     148                 :             :   std::unique_ptr<sink>
     149                 :             :   make_sink (const context &ctxt,
     150                 :             :              diagnostics::context &dc,
     151                 :             :              const char *unparsed_arg,
     152                 :             :              const scheme_name_and_params &parsed_arg);
     153                 :             : 
     154                 :             :   const scheme_handler *get_scheme_handler (const std::string &scheme_name);
     155                 :             : 
     156                 :             : private:
     157                 :             :   std::vector<std::unique_ptr<scheme_handler>> m_scheme_handlers;
     158                 :             : };
     159                 :             : 
     160                 :             : class text_scheme_handler : public output_factory::scheme_handler
     161                 :             : {
     162                 :             : public:
     163                 :          37 :   text_scheme_handler () : scheme_handler ("text") {}
     164                 :             : 
     165                 :             :   std::unique_ptr<sink>
     166                 :             :   make_sink (const context &ctxt,
     167                 :             :              diagnostics::context &dc,
     168                 :             :              const char *unparsed_arg,
     169                 :             :              const scheme_name_and_params &parsed_arg) const final override;
     170                 :             : };
     171                 :             : 
     172                 :             : class sarif_scheme_handler : public output_factory::scheme_handler
     173                 :             : {
     174                 :             : public:
     175                 :          37 :   sarif_scheme_handler () : scheme_handler ("sarif") {}
     176                 :             : 
     177                 :             :   std::unique_ptr<sink>
     178                 :             :   make_sink (const context &ctxt,
     179                 :             :              diagnostics::context &dc,
     180                 :             :              const char *unparsed_arg,
     181                 :             :              const scheme_name_and_params &parsed_arg) const final override;
     182                 :             : 
     183                 :             : private:
     184                 :             :   static std::unique_ptr<sarif_serialization_format>
     185                 :             :   make_sarif_serialization_object (enum sarif_serialization_kind);
     186                 :             : };
     187                 :             : 
     188                 :             : class html_scheme_handler : public output_factory::scheme_handler
     189                 :             : {
     190                 :             : public:
     191                 :          37 :   html_scheme_handler () : scheme_handler ("experimental-html") {}
     192                 :             : 
     193                 :             :   std::unique_ptr<sink>
     194                 :             :   make_sink (const context &ctxt,
     195                 :             :              diagnostics::context &dc,
     196                 :             :              const char *unparsed_arg,
     197                 :             :              const scheme_name_and_params &parsed_arg) const final override;
     198                 :             : };
     199                 :             : 
     200                 :             : /* struct context.  */
     201                 :             : 
     202                 :             : void
     203                 :          16 : context::report_error (const char *gmsgid, ...) const
     204                 :             : {
     205                 :          16 :   va_list ap;
     206                 :          16 :   va_start (ap, gmsgid);
     207                 :          16 :   report_error_va (gmsgid, &ap);
     208                 :          16 :   va_end (ap);
     209                 :          16 : }
     210                 :             : 
     211                 :             : void
     212                 :           0 : context::report_unknown_key (const char *unparsed_arg,
     213                 :             :                              const std::string &key,
     214                 :             :                              const std::string &scheme_name,
     215                 :             :                              auto_vec<const char *> &known_keys) const
     216                 :             : {
     217                 :           0 :   pp_markup::comma_separated_quoted_strings e (known_keys);
     218                 :           0 :   report_error
     219                 :           0 :     ("%<%s%s%>:"
     220                 :             :      " unknown key %qs for format %qs; known keys: %e",
     221                 :             :      get_option_name (), unparsed_arg,
     222                 :             :      key.c_str (), scheme_name.c_str (), &e);
     223                 :           0 : }
     224                 :             : 
     225                 :             : void
     226                 :           0 : context::report_missing_key (const char *unparsed_arg,
     227                 :             :                              const std::string &key,
     228                 :             :                              const std::string &scheme_name,
     229                 :             :                              const char *metavar) const
     230                 :             : {
     231                 :           0 :   report_error
     232                 :           0 :     ("%<%s%s%>:"
     233                 :             :      " missing required key %qs for format %qs;"
     234                 :             :      " try %<%s%s:%s=%s%>",
     235                 :             :      get_option_name (), unparsed_arg,
     236                 :             :      key.c_str (), scheme_name.c_str (),
     237                 :             :      get_option_name (), scheme_name.c_str (), key.c_str (), metavar);
     238                 :           0 : }
     239                 :             : 
     240                 :             : output_file
     241                 :           2 : context::open_output_file (label_text &&filename) const
     242                 :             : {
     243                 :           2 :   FILE *outf = fopen (filename.get (), "w");
     244                 :           2 :   if (!outf)
     245                 :             :     {
     246                 :           0 :       report_error ("unable to open %qs: %m", filename.get ());
     247                 :           0 :       return output_file (nullptr, false, std::move (filename));
     248                 :             :     }
     249                 :           2 :   return output_file (outf, true, std::move (filename));
     250                 :             : }
     251                 :             : 
     252                 :             : static std::unique_ptr<scheme_name_and_params>
     253                 :          65 : parse (const context &ctxt, const char *unparsed_arg)
     254                 :             : {
     255                 :          65 :   scheme_name_and_params result;
     256                 :          65 :   if (const char *const colon = strchr (unparsed_arg, ':'))
     257                 :             :     {
     258                 :          54 :       result.m_scheme_name = std::string (unparsed_arg, colon - unparsed_arg);
     259                 :             :       /* Expect zero of more of KEY=VALUE,KEY=VALUE, etc  .*/
     260                 :          54 :       const char *iter = colon + 1;
     261                 :          54 :       const char *last_separator = ":";
     262                 :         113 :       while (iter)
     263                 :             :         {
     264                 :             :           /* Look for a non-empty key string followed by '='.  */
     265                 :          75 :           const char *eq = strchr (iter, '=');
     266                 :          75 :           if (eq == nullptr || eq == iter)
     267                 :             :             {
     268                 :             :               /* Missing '='.  */
     269                 :          16 :               ctxt.report_error
     270                 :          16 :                 ("%<%s%s%>:"
     271                 :             :                  " expected KEY=VALUE-style parameter for format %qs"
     272                 :             :                  " after %qs;"
     273                 :             :                  " got %qs",
     274                 :             :                  ctxt.get_option_name (), unparsed_arg,
     275                 :             :                  result.m_scheme_name.c_str (),
     276                 :             :                  last_separator,
     277                 :             :                  iter);
     278                 :          16 :               return nullptr;
     279                 :             :             }
     280                 :          59 :           std::string key = std::string (iter, eq - iter);
     281                 :          59 :           std::string value;
     282                 :          59 :           const char *comma = strchr (iter, ',');
     283                 :          59 :           if (comma)
     284                 :             :             {
     285                 :          21 :               value = std::string (eq + 1, comma - (eq + 1));
     286                 :          21 :               iter = comma + 1;
     287                 :          21 :               last_separator = ",";
     288                 :             :             }
     289                 :             :           else
     290                 :             :             {
     291                 :          38 :               value = std::string (eq + 1);
     292                 :          38 :               iter = nullptr;
     293                 :             :             }
     294                 :         177 :           result.m_kvs.push_back ({std::move (key), std::move (value)});
     295                 :          59 :         }
     296                 :             :     }
     297                 :             :   else
     298                 :          11 :     result.m_scheme_name = unparsed_arg;
     299                 :          49 :   return std::make_unique<scheme_name_and_params> (std::move (result));
     300                 :          65 : }
     301                 :             : 
     302                 :             : std::unique_ptr<sink>
     303                 :          37 : context::parse_and_make_sink (const char *unparsed_arg,
     304                 :             :                               diagnostics::context &dc)
     305                 :             : {
     306                 :          37 :   auto parsed_arg = parse (*this, unparsed_arg);
     307                 :          37 :   if (!parsed_arg)
     308                 :           0 :     return nullptr;
     309                 :             : 
     310                 :          37 :   output_factory factory;
     311                 :          37 :   return factory.make_sink (*this, dc, unparsed_arg, *parsed_arg);
     312                 :          37 : }
     313                 :             : 
     314                 :             : /* class output_factory::scheme_handler.  */
     315                 :             : 
     316                 :             : /* class output_factory.  */
     317                 :             : 
     318                 :          37 : output_factory::output_factory ()
     319                 :             : {
     320                 :          37 :   m_scheme_handlers.push_back (std::make_unique<text_scheme_handler> ());
     321                 :          37 :   m_scheme_handlers.push_back (std::make_unique<sarif_scheme_handler> ());
     322                 :          37 :   m_scheme_handlers.push_back (std::make_unique<html_scheme_handler> ());
     323                 :          37 : }
     324                 :             : 
     325                 :             : const output_factory::scheme_handler *
     326                 :          37 : output_factory::get_scheme_handler (const std::string &scheme_name)
     327                 :             : {
     328                 :          75 :   for (auto &iter : m_scheme_handlers)
     329                 :          75 :     if (iter->get_scheme_name () == scheme_name)
     330                 :          37 :       return iter.get ();
     331                 :             :   return nullptr;
     332                 :             : }
     333                 :             : 
     334                 :             : std::unique_ptr<sink>
     335                 :          37 : output_factory::make_sink (const context &ctxt,
     336                 :             :                            diagnostics::context &dc,
     337                 :             :                            const char *unparsed_arg,
     338                 :             :                            const scheme_name_and_params &parsed_arg)
     339                 :             : {
     340                 :          37 :   auto scheme_handler = get_scheme_handler (parsed_arg.m_scheme_name);
     341                 :          37 :   if (!scheme_handler)
     342                 :             :     {
     343                 :           0 :       auto_vec<const char *> strings;
     344                 :           0 :       for (auto &iter : m_scheme_handlers)
     345                 :           0 :         strings.safe_push (iter->get_scheme_name ().c_str ());
     346                 :           0 :       pp_markup::comma_separated_quoted_strings e (strings);
     347                 :           0 :       ctxt.report_error ("%<%s%s%>:"
     348                 :             :                          " unrecognized format %qs; known formats: %e",
     349                 :             :                          ctxt.get_option_name (), unparsed_arg,
     350                 :             :                          parsed_arg.m_scheme_name.c_str (), &e);
     351                 :           0 :       return nullptr;
     352                 :           0 :     }
     353                 :             : 
     354                 :          37 :   return scheme_handler->make_sink (ctxt, dc, unparsed_arg, parsed_arg);
     355                 :             : }
     356                 :             : 
     357                 :             : /* class text_scheme_handler : public output_factory::scheme_handler.  */
     358                 :             : 
     359                 :             : std::unique_ptr<sink>
     360                 :          12 : text_scheme_handler::make_sink (const context &ctxt,
     361                 :             :                                 diagnostics::context &dc,
     362                 :             :                                 const char *unparsed_arg,
     363                 :             :                                 const scheme_name_and_params &parsed_arg) const
     364                 :             : {
     365                 :          12 :   bool show_color = pp_show_color (dc.get_reference_printer ());
     366                 :          12 :   bool show_nesting = true;
     367                 :          12 :   bool show_locations_in_nesting = true;
     368                 :          12 :   bool show_levels = false;
     369                 :          34 :   for (auto& iter : parsed_arg.m_kvs)
     370                 :             :     {
     371                 :          22 :       const std::string &key = iter.first;
     372                 :          22 :       const std::string &value = iter.second;
     373                 :          22 :       if (key == "color")
     374                 :             :         {
     375                 :           0 :           if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_color))
     376                 :           0 :             return nullptr;
     377                 :          22 :           continue;
     378                 :             :         }
     379                 :          22 :       if (key == "show-nesting")
     380                 :             :         {
     381                 :          12 :           if (!parse_bool_value (ctxt, unparsed_arg, key, value,
     382                 :             :                                  show_nesting))
     383                 :           0 :             return nullptr;
     384                 :          12 :           continue;
     385                 :             :         }
     386                 :          10 :       if (key == "show-nesting-locations")
     387                 :             :         {
     388                 :           9 :           if (!parse_bool_value (ctxt, unparsed_arg, key, value,
     389                 :             :                                  show_locations_in_nesting))
     390                 :           0 :             return nullptr;
     391                 :           9 :           continue;
     392                 :             :         }
     393                 :           1 :       if (key == "show-nesting-levels")
     394                 :             :         {
     395                 :           1 :           if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_levels))
     396                 :           0 :             return nullptr;
     397                 :           1 :           continue;
     398                 :             :         }
     399                 :             : 
     400                 :             :       /* Key not found.  */
     401                 :           0 :       auto_vec<const char *> known_keys;
     402                 :           0 :       known_keys.safe_push ("color");
     403                 :           0 :       known_keys.safe_push ("show-nesting");
     404                 :           0 :       known_keys.safe_push ("show-nesting-locations");
     405                 :           0 :       known_keys.safe_push ("show-nesting-levels");
     406                 :           0 :       ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
     407                 :             :                                known_keys);
     408                 :           0 :       return nullptr;
     409                 :           0 :     }
     410                 :             : 
     411                 :          12 :   auto sink = std::make_unique<diagnostics::text_sink> (dc);
     412                 :          12 :   sink->set_show_nesting (show_nesting);
     413                 :          12 :   sink->set_show_locations_in_nesting (show_locations_in_nesting);
     414                 :          12 :   sink->set_show_nesting_levels (show_levels);
     415                 :          12 :   return sink;
     416                 :          12 : }
     417                 :             : 
     418                 :             : /* class sarif_scheme_handler : public output_factory::scheme_handler.  */
     419                 :             : 
     420                 :             : std::unique_ptr<sink>
     421                 :          12 : sarif_scheme_handler::make_sink (const context &ctxt,
     422                 :             :                                  diagnostics::context &dc,
     423                 :             :                                  const char *unparsed_arg,
     424                 :             :                                  const scheme_name_and_params &parsed_arg) const
     425                 :             : {
     426                 :          12 :   label_text filename;
     427                 :          12 :   enum sarif_serialization_kind serialization_kind
     428                 :             :     = sarif_serialization_kind::json;
     429                 :          12 :   sarif_generation_options sarif_gen_opts;
     430                 :          19 :   for (auto& iter : parsed_arg.m_kvs)
     431                 :             :     {
     432                 :           7 :       const std::string &key = iter.first;
     433                 :           7 :       const std::string &value = iter.second;
     434                 :           7 :       if (key == "file")
     435                 :             :         {
     436                 :           2 :           filename = label_text::take (xstrdup (value.c_str ()));
     437                 :           7 :           continue;
     438                 :             :         }
     439                 :           5 :       if (key == "serialization")
     440                 :             :         {
     441                 :           0 :           static const std::array<std::pair<const char *, enum sarif_serialization_kind>,
     442                 :             :                                   (size_t)sarif_serialization_kind::num_values> value_names
     443                 :             :             {{{"json", sarif_serialization_kind::json}}};
     444                 :             : 
     445                 :           0 :           if (!parse_enum_value<enum sarif_serialization_kind>
     446                 :           0 :                  (ctxt, unparsed_arg,
     447                 :             :                   key, value,
     448                 :             :                   value_names,
     449                 :             :                   serialization_kind))
     450                 :           0 :             return nullptr;
     451                 :           0 :           continue;
     452                 :           0 :         }
     453                 :           5 :       if (key == "version")
     454                 :             :         {
     455                 :           3 :           static const std::array<std::pair<const char *, enum sarif_version>,
     456                 :             :                                   (size_t)sarif_version::num_versions> value_names
     457                 :             :             {{{"2.1", sarif_version::v2_1_0},
     458                 :             :               {"2.2-prerelease", sarif_version::v2_2_prerelease_2024_08_08}}};
     459                 :             : 
     460                 :           3 :             if (!parse_enum_value<enum sarif_version>
     461                 :           3 :                    (ctxt, unparsed_arg,
     462                 :             :                     key, value,
     463                 :             :                     value_names,
     464                 :             :                     sarif_gen_opts.m_version))
     465                 :           0 :             return nullptr;
     466                 :           3 :           continue;
     467                 :           3 :         }
     468                 :           2 :       if (key == "state-graphs")
     469                 :             :         {
     470                 :           2 :           if (!parse_bool_value (ctxt, unparsed_arg, key, value,
     471                 :             :                                  sarif_gen_opts.m_state_graph))
     472                 :           0 :             return nullptr;
     473                 :           2 :           continue;
     474                 :             :         }
     475                 :             : 
     476                 :             :       /* Key not found.  */
     477                 :           0 :       auto_vec<const char *> known_keys;
     478                 :           0 :       known_keys.safe_push ("file");
     479                 :           0 :       known_keys.safe_push ("serialization");
     480                 :           0 :       known_keys.safe_push ("state-graphs");
     481                 :           0 :       known_keys.safe_push ("version");
     482                 :           0 :       ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
     483                 :             :                                known_keys);
     484                 :           0 :       return nullptr;
     485                 :           0 :     }
     486                 :             : 
     487                 :          12 :   output_file output_file_;
     488                 :          12 :   if (filename.get ())
     489                 :           2 :     output_file_ = ctxt.open_output_file (std::move (filename));
     490                 :             :   else
     491                 :             :     // Default filename
     492                 :             :     {
     493                 :          10 :       const char *basename = ctxt.get_base_filename ();
     494                 :          10 :       if (!basename)
     495                 :             :         {
     496                 :           0 :           ctxt.report_missing_key (unparsed_arg,
     497                 :           0 :                                    "file",
     498                 :             :                                    get_scheme_name (),
     499                 :             :                                    "FILENAME");
     500                 :           0 :           return nullptr;
     501                 :             :         }
     502                 :          10 :       output_file_
     503                 :          20 :         = open_sarif_output_file (dc,
     504                 :             :                                   ctxt.get_affected_location_mgr (),
     505                 :             :                                   basename,
     506                 :          10 :                                   serialization_kind);
     507                 :             :     }
     508                 :          12 :   if (!output_file_)
     509                 :           0 :     return nullptr;
     510                 :             : 
     511                 :          12 :   auto serialization_obj = make_sarif_serialization_object (serialization_kind);
     512                 :             : 
     513                 :          12 :   auto sink = make_sarif_sink (dc,
     514                 :          12 :                                *ctxt.get_affected_location_mgr (),
     515                 :             :                                std::move (serialization_obj),
     516                 :             :                                sarif_gen_opts,
     517                 :          12 :                                std::move (output_file_));
     518                 :          12 :   return sink;
     519                 :          12 : }
     520                 :             : 
     521                 :             : std::unique_ptr<sarif_serialization_format>
     522                 :          12 : sarif_scheme_handler::
     523                 :             : make_sarif_serialization_object (enum sarif_serialization_kind kind)
     524                 :             : {
     525                 :          12 :   switch (kind)
     526                 :             :     {
     527                 :           0 :     default:
     528                 :           0 :       gcc_unreachable ();
     529                 :          12 :     case sarif_serialization_kind::json:
     530                 :          12 :       return std::make_unique<sarif_serialization_format_json> (true);
     531                 :             :       break;
     532                 :             :     }
     533                 :             : }
     534                 :             : 
     535                 :             : /* class html_scheme_handler : public output_factory::scheme_handler.  */
     536                 :             : 
     537                 :             : std::unique_ptr<sink>
     538                 :          13 : html_scheme_handler::make_sink (const context &ctxt,
     539                 :             :                                 diagnostics::context &dc,
     540                 :             :                                 const char *unparsed_arg,
     541                 :             :                                 const scheme_name_and_params &parsed_arg) const
     542                 :             : {
     543                 :          13 :   label_text filename;
     544                 :          13 :   html_generation_options html_gen_opts;
     545                 :          27 :   for (auto& iter : parsed_arg.m_kvs)
     546                 :             :     {
     547                 :          14 :       const std::string &key = iter.first;
     548                 :          14 :       const std::string &value = iter.second;
     549                 :          14 :       if (key == "css")
     550                 :             :         {
     551                 :           0 :           if (!parse_bool_value (ctxt, unparsed_arg, key, value,
     552                 :             :                                  html_gen_opts.m_css))
     553                 :           0 :             return nullptr;
     554                 :          14 :           continue;
     555                 :             :         }
     556                 :          14 :       if (key == "file")
     557                 :             :         {
     558                 :           0 :           filename = label_text::take (xstrdup (value.c_str ()));
     559                 :           0 :           continue;
     560                 :             :         }
     561                 :          14 :       if (key == "javascript")
     562                 :             :         {
     563                 :          13 :           if (!parse_bool_value (ctxt, unparsed_arg, key, value,
     564                 :             :                                  html_gen_opts.m_javascript))
     565                 :           0 :             return nullptr;
     566                 :          13 :           continue;
     567                 :             :         }
     568                 :           1 :       if (key == "show-state-diagrams")
     569                 :             :         {
     570                 :           1 :           if (!parse_bool_value (ctxt, unparsed_arg, key, value,
     571                 :             :                                  html_gen_opts.m_show_state_diagrams))
     572                 :           0 :             return nullptr;
     573                 :           1 :           continue;
     574                 :             :         }
     575                 :           0 :       if (key == "show-state-diagrams-dot-src")
     576                 :             :         {
     577                 :           0 :           if (!parse_bool_value (ctxt, unparsed_arg, key, value,
     578                 :             :                                  html_gen_opts.m_show_state_diagrams_dot_src))
     579                 :           0 :             return nullptr;
     580                 :           0 :           continue;
     581                 :             :         }
     582                 :           0 :       if (key == "show-state-diagrams-sarif")
     583                 :             :         {
     584                 :           0 :           if (!parse_bool_value (ctxt, unparsed_arg, key, value,
     585                 :             :                                  html_gen_opts.m_show_state_diagrams_sarif))
     586                 :           0 :             return nullptr;
     587                 :           0 :           continue;
     588                 :             :         }
     589                 :             : 
     590                 :             :       /* Key not found.  */
     591                 :           0 :       auto_vec<const char *> known_keys;
     592                 :           0 :       known_keys.safe_push ("css");
     593                 :           0 :       known_keys.safe_push ("file");
     594                 :           0 :       known_keys.safe_push ("javascript");
     595                 :           0 :       known_keys.safe_push ("show-state-diagrams");
     596                 :           0 :       known_keys.safe_push ("show-state-diagram-dot-src");
     597                 :           0 :       known_keys.safe_push ("show-state-diagram-sarif");
     598                 :           0 :       ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
     599                 :             :                                known_keys);
     600                 :           0 :       return nullptr;
     601                 :           0 :     }
     602                 :             : 
     603                 :          13 :   output_file output_file_;
     604                 :          13 :   if (filename.get ())
     605                 :           0 :     output_file_ = ctxt.open_output_file (std::move (filename));
     606                 :             :   else
     607                 :             :     // Default filename
     608                 :             :     {
     609                 :          13 :       const char *basename = ctxt.get_base_filename ();
     610                 :          13 :       if (!basename)
     611                 :             :         {
     612                 :           0 :           ctxt.report_missing_key (unparsed_arg,
     613                 :           0 :                                    "file",
     614                 :             :                                    get_scheme_name (),
     615                 :             :                                    "FILENAME");
     616                 :           0 :           return nullptr;
     617                 :             :         }
     618                 :          13 :       output_file_
     619                 :             :         = open_html_output_file
     620                 :          26 :             (dc,
     621                 :             :              ctxt.get_affected_location_mgr (),
     622                 :          13 :              basename);
     623                 :             :     }
     624                 :          13 :   if (!output_file_)
     625                 :           0 :     return nullptr;
     626                 :             : 
     627                 :          13 :   auto sink = make_html_sink (dc,
     628                 :          13 :                               *ctxt.get_affected_location_mgr (),
     629                 :             :                               html_gen_opts,
     630                 :          13 :                               std::move (output_file_));
     631                 :          13 :   return sink;
     632                 :          13 : }
     633                 :             : 
     634                 :             : } // namespace output_spec
     635                 :             : 
     636                 :             : #if CHECKING_P
     637                 :             : 
     638                 :             : namespace selftest {
     639                 :             : 
     640                 :             : using auto_fix_quotes = ::selftest::auto_fix_quotes;
     641                 :             : 
     642                 :             : /* RAII class to temporarily override "progname" to the
     643                 :             :    string "PROGNAME".  */
     644                 :             : 
     645                 :             : class auto_fix_progname
     646                 :             : {
     647                 :             : public:
     648                 :           4 :   auto_fix_progname ()
     649                 :           4 :   {
     650                 :           4 :     m_old_progname = progname;
     651                 :           4 :     progname = "PROGNAME";
     652                 :             :   }
     653                 :             : 
     654                 :           4 :   ~auto_fix_progname ()
     655                 :             :   {
     656                 :           4 :     progname = m_old_progname;
     657                 :           4 :   }
     658                 :             : 
     659                 :             : private:
     660                 :             :   const char *m_old_progname;
     661                 :             : };
     662                 :             : 
     663                 :          56 : struct parser_test
     664                 :             : {
     665                 :          28 :   class test_spec_context : public diagnostics::output_spec::dc_spec_context
     666                 :             :   {
     667                 :             :   public:
     668                 :          28 :     test_spec_context (diagnostics::context &dc,
     669                 :             :                        line_maps *location_mgr,
     670                 :             :                        location_t loc,
     671                 :             :                        const char *option_name)
     672                 :             :     : dc_spec_context (dc,
     673                 :             :                        location_mgr,
     674                 :             :                        location_mgr,
     675                 :             :                        loc,
     676                 :          28 :                        option_name)
     677                 :             :     {
     678                 :             :     }
     679                 :             : 
     680                 :             :     const char *
     681                 :           0 :     get_base_filename () const final override
     682                 :             :     {
     683                 :           0 :       return "BASE_FILENAME";
     684                 :             :     }
     685                 :             :   };
     686                 :             : 
     687                 :          28 :   parser_test ()
     688                 :          28 :   : m_dc (),
     689                 :          28 :     m_ctxt (m_dc, line_table, UNKNOWN_LOCATION, "-fOPTION="),
     690                 :          28 :     m_fmt (m_dc.get_sink (0))
     691                 :             :   {
     692                 :          28 :     pp_buffer (m_fmt.get_printer ())->m_flush_p = false;
     693                 :          28 :   }
     694                 :             : 
     695                 :             :   std::unique_ptr<diagnostics::output_spec::scheme_name_and_params>
     696                 :          28 :   parse (const char *unparsed_arg)
     697                 :             :   {
     698                 :          28 :     return diagnostics::output_spec::parse (m_ctxt, unparsed_arg);
     699                 :             :   }
     700                 :             : 
     701                 :          28 :   bool execution_failed_p () const
     702                 :             :   {
     703                 :          28 :     return m_dc.execution_failed_p ();
     704                 :             :   }
     705                 :             : 
     706                 :             :   const char *
     707                 :          16 :   get_diagnostic_text () const
     708                 :             :   {
     709                 :          16 :     return pp_formatted_text (m_fmt.get_printer ());
     710                 :             :   }
     711                 :             : 
     712                 :             : private:
     713                 :             :   diagnostics::selftest::test_context m_dc;
     714                 :             :   test_spec_context m_ctxt;
     715                 :             :   diagnostics::sink &m_fmt;
     716                 :             : };
     717                 :             : 
     718                 :             : /* Selftests.  */
     719                 :             : 
     720                 :             : static void
     721                 :           4 : test_output_arg_parsing ()
     722                 :             : {
     723                 :           4 :   auto_fix_quotes fix_quotes;
     724                 :           4 :   auto_fix_progname fix_progname;
     725                 :             : 
     726                 :             :   /* Minimal correct example.  */
     727                 :           4 :   {
     728                 :           4 :     parser_test pt;
     729                 :           4 :     auto result = pt.parse ("foo");
     730                 :           4 :     ASSERT_EQ (result->m_scheme_name, "foo");
     731                 :           4 :     ASSERT_EQ (result->m_kvs.size (), 0);
     732                 :           4 :     ASSERT_FALSE (pt.execution_failed_p ());
     733                 :           4 :   }
     734                 :             : 
     735                 :             :   /* Stray trailing colon with no key/value pairs.  */
     736                 :           4 :   {
     737                 :           4 :     parser_test pt;
     738                 :           4 :     auto result = pt.parse ("foo:");
     739                 :           4 :     ASSERT_EQ (result, nullptr);
     740                 :           4 :     ASSERT_TRUE (pt.execution_failed_p ());
     741                 :           4 :     ASSERT_STREQ (pt.get_diagnostic_text (),
     742                 :             :                   "PROGNAME: error: `-fOPTION=foo:':"
     743                 :             :                   " expected KEY=VALUE-style parameter for format `foo'"
     744                 :             :                   " after `:';"
     745                 :             :                   " got `'\n");
     746                 :           4 :   }
     747                 :             : 
     748                 :             :   /* No key before '='.  */
     749                 :           4 :   {
     750                 :           4 :     parser_test pt;
     751                 :           4 :     auto result = pt.parse ("foo:=");
     752                 :           4 :     ASSERT_EQ (result, nullptr);
     753                 :           4 :     ASSERT_TRUE (pt.execution_failed_p ());
     754                 :           4 :     ASSERT_STREQ (pt.get_diagnostic_text (),
     755                 :             :                   "PROGNAME: error: `-fOPTION=foo:=':"
     756                 :             :                   " expected KEY=VALUE-style parameter for format `foo'"
     757                 :             :                   " after `:';"
     758                 :             :                   " got `='\n");
     759                 :           4 :   }
     760                 :             : 
     761                 :             :   /* No value for key.  */
     762                 :           4 :   {
     763                 :           4 :     parser_test pt;
     764                 :           4 :     auto result = pt.parse ("foo:key,");
     765                 :           4 :     ASSERT_EQ (result, nullptr);
     766                 :           4 :     ASSERT_TRUE (pt.execution_failed_p ());
     767                 :           4 :     ASSERT_STREQ (pt.get_diagnostic_text (),
     768                 :             :                   "PROGNAME: error: `-fOPTION=foo:key,':"
     769                 :             :                   " expected KEY=VALUE-style parameter for format `foo'"
     770                 :             :                   " after `:';"
     771                 :             :                   " got `key,'\n");
     772                 :           4 :   }
     773                 :             : 
     774                 :             :   /* Correct example, with one key/value pair.  */
     775                 :           4 :   {
     776                 :           4 :     parser_test pt;
     777                 :           4 :     auto result = pt.parse ("foo:key=value");
     778                 :           4 :     ASSERT_EQ (result->m_scheme_name, "foo");
     779                 :           4 :     ASSERT_EQ (result->m_kvs.size (), 1);
     780                 :           4 :     ASSERT_EQ (result->m_kvs[0].first, "key");
     781                 :           4 :     ASSERT_EQ (result->m_kvs[0].second, "value");
     782                 :           4 :     ASSERT_FALSE (pt.execution_failed_p ());
     783                 :           4 :   }
     784                 :             : 
     785                 :             :   /* Stray trailing comma.  */
     786                 :           4 :   {
     787                 :           4 :     parser_test pt;
     788                 :           4 :     auto result = pt.parse ("foo:key=value,");
     789                 :           4 :     ASSERT_EQ (result, nullptr);
     790                 :           4 :     ASSERT_TRUE (pt.execution_failed_p ());
     791                 :           4 :     ASSERT_STREQ (pt.get_diagnostic_text (),
     792                 :             :                   "PROGNAME: error: `-fOPTION=foo:key=value,':"
     793                 :             :                   " expected KEY=VALUE-style parameter for format `foo'"
     794                 :             :                   " after `,';"
     795                 :             :                   " got `'\n");
     796                 :           4 :   }
     797                 :             : 
     798                 :             :   /* Correct example, with two key/value pairs.  */
     799                 :           4 :   {
     800                 :           4 :     parser_test pt;
     801                 :           4 :     auto result = pt.parse ("foo:color=red,shape=circle");
     802                 :           4 :     ASSERT_EQ (result->m_scheme_name, "foo");
     803                 :           4 :     ASSERT_EQ (result->m_kvs.size (), 2);
     804                 :           4 :     ASSERT_EQ (result->m_kvs[0].first, "color");
     805                 :           4 :     ASSERT_EQ (result->m_kvs[0].second, "red");
     806                 :           4 :     ASSERT_EQ (result->m_kvs[1].first, "shape");
     807                 :           4 :     ASSERT_EQ (result->m_kvs[1].second, "circle");
     808                 :           4 :     ASSERT_FALSE (pt.execution_failed_p ());
     809                 :           4 :   }
     810                 :           4 : }
     811                 :             : 
     812                 :             : /* Run all of the selftests within this file.  */
     813                 :             : 
     814                 :             : void
     815                 :           4 : output_spec_cc_tests ()
     816                 :             : {
     817                 :           4 :   test_output_arg_parsing ();
     818                 :           4 : }
     819                 :             : 
     820                 :             : } // namespace diagnostics::selftest
     821                 :             : 
     822                 :             : #endif /* #if CHECKING_P */
     823                 :             : 
     824                 :             : } // 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.