LCOV - code coverage report
Current view: top level - gcc/diagnostics - html-sink.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 79.7 % 798 636
Test Date: 2025-09-20 13:40:47 Functions: 73.5 % 68 50
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* HTML output for diagnostics.
       2                 :             :    Copyright (C) 2024-2025 Free Software Foundation, Inc.
       3                 :             :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       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                 :             : #include "config.h"
      22                 :             : #define INCLUDE_MAP
      23                 :             : #define INCLUDE_STRING
      24                 :             : #define INCLUDE_VECTOR
      25                 :             : #include "system.h"
      26                 :             : #include "coretypes.h"
      27                 :             : #include "diagnostics/metadata.h"
      28                 :             : #include "diagnostics/sink.h"
      29                 :             : #include "diagnostics/html-sink.h"
      30                 :             : #include "diagnostics/text-sink.h"
      31                 :             : #include "diagnostics/sarif-sink.h"
      32                 :             : #include "diagnostics/output-file.h"
      33                 :             : #include "diagnostics/buffering.h"
      34                 :             : #include "diagnostics/paths.h"
      35                 :             : #include "diagnostics/dumping.h"
      36                 :             : #include "diagnostics/logging.h"
      37                 :             : #include "diagnostics/client-data-hooks.h"
      38                 :             : #include "selftest.h"
      39                 :             : #include "diagnostics/selftest-context.h"
      40                 :             : #include "pretty-print-format-impl.h"
      41                 :             : #include "pretty-print-urlifier.h"
      42                 :             : #include "diagnostics/changes.h"
      43                 :             : #include "intl.h"
      44                 :             : #include "xml.h"
      45                 :             : #include "xml-printer.h"
      46                 :             : #include "diagnostics/digraphs.h"
      47                 :             : #include "diagnostics/state-graphs.h"
      48                 :             : #include "graphviz.h"
      49                 :             : #include "json.h"
      50                 :             : #include "selftest-xml.h"
      51                 :             : 
      52                 :             : namespace diagnostics {
      53                 :             : 
      54                 :             : // struct html_generation_options
      55                 :             : 
      56                 :          21 : html_generation_options::html_generation_options ()
      57                 :          21 : : m_css (true),
      58                 :          21 :   m_javascript (true),
      59                 :          21 :   m_show_state_diagrams (false),
      60                 :          21 :   m_show_state_diagrams_sarif (false),
      61                 :          21 :   m_show_state_diagrams_dot_src (false)
      62                 :             : {
      63                 :          21 : }
      64                 :             : 
      65                 :             : void
      66                 :           0 : html_generation_options::dump (FILE *outfile, int indent) const
      67                 :             : {
      68                 :           0 :   DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_css);
      69                 :           0 :   DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_javascript);
      70                 :           0 :   DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams);
      71                 :           0 :   DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_sarif);
      72                 :           0 :   DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_dot_src);
      73                 :           0 : }
      74                 :             : 
      75                 :             : class html_builder;
      76                 :             : 
      77                 :             : /* Concrete buffering implementation subclass for HTML output.  */
      78                 :             : 
      79                 :             : class html_sink_buffer : public per_sink_buffer
      80                 :             : {
      81                 :             : public:
      82                 :             :   friend class html_builder;
      83                 :             :   friend class html_sink;
      84                 :             : 
      85                 :           0 :   html_sink_buffer (html_builder &builder)
      86                 :           0 :   : m_builder (builder)
      87                 :             :   {}
      88                 :             : 
      89                 :             :   void dump (FILE *out, int indent) const final override;
      90                 :             :   bool empty_p () const final override;
      91                 :             :   void move_to (per_sink_buffer &dest) final override;
      92                 :             :   void clear () final override;
      93                 :             :   void flush () final override;
      94                 :             : 
      95                 :             :   void add_result (std::unique_ptr<xml::element> result)
      96                 :             :   {
      97                 :             :     m_results.push_back (std::move (result));
      98                 :             :   }
      99                 :             : 
     100                 :             : private:
     101                 :             :   html_builder &m_builder;
     102                 :             :   std::vector<std::unique_ptr<xml::element>> m_results;
     103                 :             : };
     104                 :             : 
     105                 :             : /* A class for managing HTML output of diagnostics.
     106                 :             : 
     107                 :             :    Implemented:
     108                 :             :    - message text
     109                 :             : 
     110                 :             :    Known limitations/missing functionality:
     111                 :             :    - title for page
     112                 :             :    - file/line/column
     113                 :             :    - error vs warning
     114                 :             :    - CWEs
     115                 :             :    - rules
     116                 :             :    - fix-it hints
     117                 :             :    - paths
     118                 :             : */
     119                 :             : 
     120                 :             : class html_builder
     121                 :             : {
     122                 :             : public:
     123                 :             :   friend class html_sink_buffer;
     124                 :             : 
     125                 :             :   html_builder (context &dc,
     126                 :             :                 pretty_printer &pp,
     127                 :             :                 const line_maps *line_maps,
     128                 :             :                 const html_generation_options &html_gen_opts);
     129                 :             : 
     130                 :             :   void dump (FILE *out, int indent) const;
     131                 :             : 
     132                 :             :   void
     133                 :             :   set_main_input_filename (const char *name);
     134                 :             : 
     135                 :             :   void on_report_diagnostic (const diagnostic_info &diagnostic,
     136                 :             :                              enum kind orig_diag_kind,
     137                 :             :                              html_sink_buffer *buffer);
     138                 :             :   void emit_diagram (const diagram &d);
     139                 :             :   void emit_global_graph (const lazily_created<digraphs::digraph> &);
     140                 :             : 
     141                 :             :   void end_group ();
     142                 :             : 
     143                 :          21 :   std::unique_ptr<xml::element> take_current_diagnostic ()
     144                 :             :   {
     145                 :          21 :     return std::move (m_cur_diagnostic_element);
     146                 :             :   }
     147                 :             : 
     148                 :             :   void flush_to_file (FILE *outf);
     149                 :             : 
     150                 :           4 :   const xml::document &get_document () const { return *m_document; }
     151                 :             : 
     152                 :          21 :   void set_printer (pretty_printer &pp)
     153                 :             :   {
     154                 :          21 :     m_printer = &pp;
     155                 :             :   }
     156                 :             : 
     157                 :             :   std::unique_ptr<xml::element>
     158                 :             :   make_element_for_metadata (const metadata &);
     159                 :             : 
     160                 :             :   std::unique_ptr<xml::element>
     161                 :             :   make_element_for_patch (const diagnostic_info &diagnostic);
     162                 :             : 
     163                 :         119 :   void add_focus_id (std::string focus_id)
     164                 :             :   {
     165                 :         119 :     m_ui_focus_ids.append_string (focus_id.c_str ());
     166                 :             :   }
     167                 :             : 
     168                 :             :   std::unique_ptr<xml::node>
     169                 :             :   maybe_make_state_diagram (const paths::event &event);
     170                 :             : 
     171                 :             : private:
     172                 :             :   void
     173                 :             :   add_stylesheet (std::string url);
     174                 :             : 
     175                 :             :   std::unique_ptr<xml::element>
     176                 :             :   make_element_for_diagnostic (const diagnostic_info &diagnostic,
     177                 :             :                                enum kind orig_diag_kind,
     178                 :             :                                bool alert);
     179                 :             : 
     180                 :             :   std::unique_ptr<xml::element>
     181                 :             :   make_metadata_element (label_text label,
     182                 :             :                          label_text url);
     183                 :             : 
     184                 :             :   void
     185                 :             :   add_at_nesting_level (size_t nesting_level,
     186                 :             :                         std::unique_ptr<xml::element> child_diag_element);
     187                 :             : 
     188                 :             :   void
     189                 :             :   push_nesting_level ();
     190                 :             : 
     191                 :             :   void
     192                 :             :   pop_nesting_level ();
     193                 :             : 
     194                 :             :   void
     195                 :             :   add_graph (const digraphs::digraph &dg,
     196                 :             :              xml::element &parent_element);
     197                 :             : 
     198                 :             :   context &m_context;
     199                 :             :   pretty_printer *m_printer;
     200                 :             :   const line_maps *m_line_maps;
     201                 :             :   html_generation_options m_html_gen_opts;
     202                 :             :   const logical_locations::manager *m_logical_loc_mgr;
     203                 :             : 
     204                 :             :   std::unique_ptr<xml::document> m_document;
     205                 :             :   xml::element *m_head_element;
     206                 :             :   xml::element *m_title_element;
     207                 :             :   xml::element *m_body_element;
     208                 :             :   xml::element *m_diagnostics_element;
     209                 :             :   std::unique_ptr<xml::element> m_cur_diagnostic_element;
     210                 :             :   std::vector<xml::element *> m_cur_nesting_levels;
     211                 :             :   int m_next_diag_id; // for handing out unique IDs
     212                 :             :   json::array m_ui_focus_ids;
     213                 :             :   logical_locations::key m_last_logical_location;
     214                 :             :   location_t m_last_location;
     215                 :             :   expanded_location m_last_expanded_location;
     216                 :             : };
     217                 :             : 
     218                 :             : static std::unique_ptr<xml::element>
     219                 :         127 : make_div (std::string class_)
     220                 :             : {
     221                 :         127 :   auto div = std::make_unique<xml::element> ("div", false);
     222                 :         127 :   div->set_attr ("class", std::move (class_));
     223                 :         127 :   return div;
     224                 :             : }
     225                 :             : 
     226                 :             : static std::unique_ptr<xml::element>
     227                 :          79 : make_span (std::string class_)
     228                 :             : {
     229                 :          79 :   auto span = std::make_unique<xml::element> ("span", true);
     230                 :          79 :   span->set_attr ("class", std::move (class_));
     231                 :          79 :   return span;
     232                 :             : }
     233                 :             : 
     234                 :             : /* class html_sink_buffer : public per_sink_buffer.  */
     235                 :             : 
     236                 :             : void
     237                 :           0 : html_sink_buffer::dump (FILE *out, int indent) const
     238                 :             : {
     239                 :           0 :   dumping::emit_heading (out, indent, "html_sink_buffer");
     240                 :           0 :   int idx = 0;
     241                 :           0 :   for (auto &result : m_results)
     242                 :             :     {
     243                 :           0 :       dumping::emit_indent (out, indent + 2);
     244                 :           0 :       fprintf (out, "result[%i]:\n", idx);
     245                 :           0 :       result->dump (out);
     246                 :           0 :       fprintf (out, "\n");
     247                 :           0 :       ++idx;
     248                 :             :     }
     249                 :           0 : }
     250                 :             : 
     251                 :             : bool
     252                 :           0 : html_sink_buffer::empty_p () const
     253                 :             : {
     254                 :           0 :   return m_results.empty ();
     255                 :             : }
     256                 :             : 
     257                 :             : void
     258                 :           0 : html_sink_buffer::move_to (per_sink_buffer &base)
     259                 :             : {
     260                 :           0 :   html_sink_buffer &dest
     261                 :             :     = static_cast<html_sink_buffer &> (base);
     262                 :           0 :   for (auto &&result : m_results)
     263                 :           0 :     dest.m_results.push_back (std::move (result));
     264                 :           0 :   m_results.clear ();
     265                 :           0 : }
     266                 :             : 
     267                 :             : void
     268                 :           0 : html_sink_buffer::clear ()
     269                 :             : {
     270                 :           0 :   m_results.clear ();
     271                 :           0 : }
     272                 :             : 
     273                 :             : void
     274                 :           0 : html_sink_buffer::flush ()
     275                 :             : {
     276                 :           0 :   for (auto &&result : m_results)
     277                 :           0 :     m_builder.m_diagnostics_element->add_child (std::move (result));
     278                 :           0 :   m_results.clear ();
     279                 :           0 : }
     280                 :             : 
     281                 :             : /* class html_builder.  */
     282                 :             : 
     283                 :             : /* Style information for writing out HTML paths.
     284                 :             :    Colors taken from https://pf3.patternfly.org/v3/styles/color-palette/ */
     285                 :             : 
     286                 :             : static const char * const HTML_STYLE
     287                 :             :   = ("  <style>\n"
     288                 :             :      "    .linenum { color: white;\n"
     289                 :             :      "               background-color: #0088ce;\n"
     290                 :             :      "               white-space: pre;\n"
     291                 :             :      "               border-right: 1px solid black; }\n"
     292                 :             :      "    .ruler { color: red;\n"
     293                 :             :      "              white-space: pre; }\n"
     294                 :             :      "    .source { color: blue;\n"
     295                 :             :      "              background-color: white;\n"
     296                 :             :      "              white-space: pre; }\n"
     297                 :             :      "    .annotation { color: green;\n"
     298                 :             :      "                  background-color: white;\n"
     299                 :             :      "                  white-space: pre; }\n"
     300                 :             :      "    .linenum-gap { text-align: center;\n"
     301                 :             :      "                   border-top: 1px solid black;\n"
     302                 :             :      "                   border-right: 1px solid black;\n"
     303                 :             :      "                   background-color: #ededed; }\n"
     304                 :             :      "    .source-gap { border-bottom: 1px dashed black;\n"
     305                 :             :      "                  border-top: 1px dashed black;\n"
     306                 :             :      "                  background-color: #ededed; }\n"
     307                 :             :      "    .no-locus-event { font-family: monospace;\n"
     308                 :             :      "                      color: green;\n"
     309                 :             :      "                      white-space: pre; }\n"
     310                 :             :      "    .funcname { font-weight: bold; }\n"
     311                 :             :      "    .events-hdr { color: white;\n"
     312                 :             :      "                  background-color: #030303; }\n"
     313                 :             :      "    .event-range {  border: 1px solid black;\n"
     314                 :             :      "                    padding: 0px; }\n"
     315                 :             :      "    .event-range-with-margin { border-spacing: 0; }\n"
     316                 :             :      "    .locus { font-family: monospace;\n"
     317                 :             :      "             border-spacing: 0px; }\n"
     318                 :             :      "    .selected { color: white;\n"
     319                 :             :      "                background-color: #0088ce; }\n"
     320                 :             :      "    .stack-frame-with-margin { border-spacing: 0; }\n"
     321                 :             :      "    .stack-frame {  padding: 5px;\n"
     322                 :             :      "                    box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.5); }\n"
     323                 :             :      "    .frame-funcname { text-align: right;\n"
     324                 :             :      "                      font-style: italic; } \n"
     325                 :             :      "    .highlight-a { color: #703fec;\n" // pf-purple-400
     326                 :             :      "                   font-weight: bold; }\n"
     327                 :             :      "    .highlight-b { color: #3f9c35;\n" // pf-green-400
     328                 :             :      "                   font-weight: bold; }\n"
     329                 :             :      "    .gcc-quoted-text { font-weight: bold;\n"
     330                 :             :      "                       font-family: mono; }\n"
     331                 :             :      "  </style>\n");
     332                 :             : 
     333                 :             : /* A little JavaScript for ease of navigation.
     334                 :             :    Keys j/k move forward and backward cyclically through a list
     335                 :             :    of focus ids (written out in another <script> tag as the HTML
     336                 :             :    is flushed).  */
     337                 :             : 
     338                 :             : const char * const HTML_SCRIPT
     339                 :             :   = ("  var current_focus_idx = 0;\n"
     340                 :             :      "\n"
     341                 :             :      "  function get_focus_span (focus_idx)\n"
     342                 :             :      "  {\n"
     343                 :             :      "      const element_id = focus_ids[focus_idx];\n"
     344                 :             :      "      return document.getElementById(element_id);\n"
     345                 :             :      "  }\n"
     346                 :             :      "  function get_any_state_diagram (focus_idx)\n"
     347                 :             :      "  {\n"
     348                 :             :      "      const element_id = focus_ids[focus_idx];\n"
     349                 :             :      "      return document.getElementById(element_id + \"-state-diagram\");\n"
     350                 :             :      "  }\n"
     351                 :             :      "  function unhighlight_current_focus_idx ()\n"
     352                 :             :      "  {\n"
     353                 :             :      "      get_focus_span (current_focus_idx).classList.remove ('selected');\n"
     354                 :             :      "      state_diagram = get_any_state_diagram (current_focus_idx);\n"
     355                 :             :      "      if (state_diagram) {\n"
     356                 :             :      "          state_diagram.style.visibility = \"hidden\";\n"
     357                 :             :      "      }\n"
     358                 :             :      "  }\n"
     359                 :             :      "  function highlight_current_focus_idx ()\n"
     360                 :             :      "  {\n"
     361                 :             :      "      const el = get_focus_span (current_focus_idx);\n"
     362                 :             :      "      el.classList.add ('selected');\n"
     363                 :             :      "      state_diagram = get_any_state_diagram (current_focus_idx);\n"
     364                 :             :      "      if (state_diagram) {\n"
     365                 :             :      "          state_diagram.style.visibility = \"visible\";\n"
     366                 :             :      "      }\n"
     367                 :             :      "      // Center the element on the screen\n"
     368                 :             :      "      const top_y = el.getBoundingClientRect ().top + window.pageYOffset;\n"
     369                 :             :      "      const middle = top_y - (window.innerHeight / 2);\n"
     370                 :             :      "      window.scrollTo (0, middle);\n"
     371                 :             :      "  }\n"
     372                 :             :      "  function select_prev_focus_idx ()\n"
     373                 :             :      "  {\n"
     374                 :             :      "      unhighlight_current_focus_idx ();\n"
     375                 :             :      "      if (current_focus_idx > 0)\n"
     376                 :             :      "          current_focus_idx -= 1;\n"
     377                 :             :      "      else\n"
     378                 :             :      "          current_focus_idx = focus_ids.length - 1;\n"
     379                 :             :      "      highlight_current_focus_idx ();\n"
     380                 :             :      "  }\n"
     381                 :             :      "  function select_next_focus_idx ()\n"
     382                 :             :      "  {\n"
     383                 :             :      "      unhighlight_current_focus_idx ();\n"
     384                 :             :      "      if (current_focus_idx < focus_ids.length - 1)\n"
     385                 :             :      "          current_focus_idx += 1;\n"
     386                 :             :      "      else\n"
     387                 :             :      "          current_focus_idx = 0;\n"
     388                 :             :      "      highlight_current_focus_idx ();\n"
     389                 :             :      "  }\n"
     390                 :             :      "  document.addEventListener('keydown', function (ev) {\n"
     391                 :             :      "      if (ev.key == 'j')\n"
     392                 :             :      "          select_next_focus_idx ();\n"
     393                 :             :      "      else if (ev.key == 'k')\n"
     394                 :             :      "          select_prev_focus_idx ();\n"
     395                 :             :      "  });\n"
     396                 :             :      "  highlight_current_focus_idx ();\n");
     397                 :             : 
     398                 :          21 : struct html_doctypedecl : public xml::doctypedecl
     399                 :             : {
     400                 :          17 :   void write_as_xml (pretty_printer *pp,
     401                 :             :                      int depth, bool indent) const final override
     402                 :             :   {
     403                 :          17 :     if (indent)
     404                 :             :       {
     405                 :          17 :         for (int i = 0; i < depth; ++i)
     406                 :           0 :           pp_string (pp, "  ");
     407                 :             :       }
     408                 :          17 :     pp_string (pp, "<!DOCTYPE html\n"
     409                 :             :                "     PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n"
     410                 :             :                "     \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
     411                 :          17 :     if (indent)
     412                 :          17 :       pp_newline (pp);
     413                 :          17 :   }
     414                 :             : };
     415                 :             : 
     416                 :             : /* html_builder's ctor.  */
     417                 :             : 
     418                 :          21 : html_builder::html_builder (context &dc,
     419                 :             :                             pretty_printer &pp,
     420                 :             :                             const line_maps *line_maps,
     421                 :          21 :                             const html_generation_options &html_gen_opts)
     422                 :          21 : : m_context (dc),
     423                 :          21 :   m_printer (&pp),
     424                 :          21 :   m_line_maps (line_maps),
     425                 :          21 :   m_html_gen_opts (html_gen_opts),
     426                 :          21 :   m_logical_loc_mgr (nullptr),
     427                 :          21 :   m_head_element (nullptr),
     428                 :          21 :   m_title_element (nullptr),
     429                 :          21 :   m_body_element (nullptr),
     430                 :          21 :   m_diagnostics_element (nullptr),
     431                 :          21 :   m_next_diag_id (0),
     432                 :          21 :   m_last_location (UNKNOWN_LOCATION),
     433                 :          21 :   m_last_expanded_location ({})
     434                 :             : {
     435                 :          21 :   gcc_assert (m_line_maps);
     436                 :             : 
     437                 :          21 :   if (auto client_data_hooks = dc.get_client_data_hooks ())
     438                 :          13 :     m_logical_loc_mgr = client_data_hooks->get_logical_location_manager ();
     439                 :             : 
     440                 :          21 :   m_document = std::make_unique<xml::document> ();
     441                 :          21 :   m_document->m_doctypedecl = std::make_unique<html_doctypedecl> ();
     442                 :          21 :   {
     443                 :          21 :     auto html_element = std::make_unique<xml::element> ("html", false);
     444                 :          21 :     html_element->set_attr ("xmlns",
     445                 :          21 :                             "http://www.w3.org/1999/xhtml");
     446                 :          21 :     xml::printer xp (*html_element.get ());
     447                 :          21 :     m_document->add_child (std::move (html_element));
     448                 :             : 
     449                 :          21 :     {
     450                 :          21 :       xml::auto_print_element head (xp, "head");
     451                 :          21 :       m_head_element = xp.get_insertion_point ();
     452                 :          21 :       {
     453                 :          21 :         xml::auto_print_element title (xp, "title", true);
     454                 :          21 :         m_title_element = xp.get_insertion_point ();
     455                 :          21 :         m_title_element->add_text (" ");
     456                 :          21 :       }
     457                 :             : 
     458                 :          21 :       if (m_html_gen_opts.m_css)
     459                 :             :         {
     460                 :          13 :           add_stylesheet ("https://cdnjs.cloudflare.com/ajax/libs/patternfly/3.24.0/css/patternfly.min.css");
     461                 :          13 :           add_stylesheet ("https://cdnjs.cloudflare.com/ajax/libs/patternfly/3.24.0/css/patternfly-additions.min.css");
     462                 :          13 :           xp.add_raw (HTML_STYLE);
     463                 :             :         }
     464                 :          21 :       if (m_html_gen_opts.m_javascript)
     465                 :             :         {
     466                 :           0 :           xp.push_tag ("script");
     467                 :             :           /* Escaping rules are different for HTML <script> elements,
     468                 :             :              so add the script "raw" for now.  */
     469                 :           0 :           xp.add_raw (HTML_SCRIPT);
     470                 :           0 :           xp.pop_tag ("script");
     471                 :             :         }
     472                 :          21 :     }
     473                 :             : 
     474                 :          21 :     {
     475                 :          21 :       xml::auto_print_element body (xp, "body");
     476                 :          21 :       m_body_element = xp.get_insertion_point ();
     477                 :          21 :       {
     478                 :          21 :         auto diagnostics_element = make_div ("gcc-diagnostic-list");
     479                 :          21 :         m_diagnostics_element = diagnostics_element.get ();
     480                 :          21 :         xp.append (std::move (diagnostics_element));
     481                 :          21 :       }
     482                 :          21 :     }
     483                 :          21 :   }
     484                 :          21 : }
     485                 :             : 
     486                 :             : void
     487                 :           0 : html_builder::dump (FILE *out, int indent) const
     488                 :             : {
     489                 :           0 :   dumping::emit_heading (out, indent, "HTML generation options");
     490                 :           0 :   m_html_gen_opts.dump (out, indent + 2);
     491                 :           0 : }
     492                 :             : 
     493                 :             : void
     494                 :          21 : html_builder::set_main_input_filename (const char *name)
     495                 :             : {
     496                 :          21 :   gcc_assert (m_title_element);
     497                 :          21 :   if (name)
     498                 :             :     {
     499                 :          21 :       m_title_element->m_children.clear ();
     500                 :          21 :       m_title_element->add_text (name);
     501                 :             :     }
     502                 :          21 : }
     503                 :             : 
     504                 :             : void
     505                 :          26 : html_builder::add_stylesheet (std::string url)
     506                 :             : {
     507                 :          26 :   gcc_assert (m_head_element);
     508                 :             : 
     509                 :          26 :   xml::printer xp (*m_head_element);
     510                 :          26 :   xp.push_tag ("link", false);
     511                 :          26 :   xp.set_attr ("rel", "stylesheet");
     512                 :          26 :   xp.set_attr ("type", "text/css");
     513                 :          26 :   xp.set_attr ("href", std::move (url));
     514                 :          26 : }
     515                 :             : 
     516                 :             : /* Implementation of "on_report_diagnostic" for HTML output.  */
     517                 :             : 
     518                 :             : void
     519                 :         106 : html_builder::on_report_diagnostic (const diagnostic_info &diagnostic,
     520                 :             :                                     enum kind orig_diag_kind,
     521                 :             :                                     html_sink_buffer *buffer)
     522                 :             : {
     523                 :         106 :   if (diagnostic.m_kind == kind::ice || diagnostic.m_kind == kind::ice_nobt)
     524                 :             :     {
     525                 :             :       /* Print a header for the remaining output to stderr, and
     526                 :             :          return, attempting to print the usual ICE messages to
     527                 :             :          stderr.  Hopefully this will be helpful to the user in
     528                 :             :          indicating what's gone wrong (also for DejaGnu, for pruning
     529                 :             :          those messages).   */
     530                 :           2 :       fnotice (stderr, "Internal compiler error:\n");
     531                 :             :     }
     532                 :             : 
     533                 :         106 :   const int nesting_level = m_context.get_diagnostic_nesting_level ();
     534                 :         106 :   bool alert = true;
     535                 :         106 :   if (m_cur_diagnostic_element && nesting_level > 0)
     536                 :             :     alert = false;
     537                 :         106 :   if (!m_cur_diagnostic_element)
     538                 :          77 :     m_last_logical_location = logical_locations::key ();
     539                 :         106 :   auto diag_element
     540                 :         106 :     = make_element_for_diagnostic (diagnostic, orig_diag_kind, alert);
     541                 :         106 :   if (buffer)
     542                 :             :     {
     543                 :           0 :       gcc_assert (!m_cur_diagnostic_element);
     544                 :           0 :       buffer->m_results.push_back (std::move (diag_element));
     545                 :             :     }
     546                 :             :   else
     547                 :             :     {
     548                 :         106 :       if (m_cur_diagnostic_element)
     549                 :             :         {
     550                 :             :           /* Nested diagnostic.  */
     551                 :          29 :           gcc_assert (nesting_level >= 0);
     552                 :          29 :           add_at_nesting_level (nesting_level, std::move (diag_element));
     553                 :             :         }
     554                 :             :       else
     555                 :             :         /* Top-level diagnostic.  */
     556                 :             :         {
     557                 :          77 :           m_cur_diagnostic_element = std::move (diag_element);
     558                 :          77 :           m_cur_nesting_levels.clear ();
     559                 :             :         }
     560                 :             :     }
     561                 :         106 : }
     562                 :             : 
     563                 :             : // For ease of comparison with show-nesting-levels=yes
     564                 :             : 
     565                 :             : static void
     566                 :          20 : add_nesting_level_attr (xml::element &element,
     567                 :             :                         int nesting_level)
     568                 :             : {
     569                 :          20 :   element.set_attr ("nesting-level", std::to_string (nesting_level));
     570                 :          20 : }
     571                 :             : 
     572                 :             : void
     573                 :          29 : html_builder::
     574                 :             : add_at_nesting_level (size_t nesting_level,
     575                 :             :                       std::unique_ptr<xml::element> child_diag_element)
     576                 :             : {
     577                 :          29 :   gcc_assert (m_cur_diagnostic_element);
     578                 :          35 :   while (nesting_level > m_cur_nesting_levels.size ())
     579                 :           6 :     push_nesting_level ();
     580                 :          31 :   while (nesting_level < m_cur_nesting_levels.size ())
     581                 :           2 :     pop_nesting_level ();
     582                 :             : 
     583                 :          29 :   if (nesting_level > 0)
     584                 :             :     {
     585                 :          14 :       gcc_assert (!m_cur_nesting_levels.empty ());
     586                 :          14 :       auto current_nesting_level = m_cur_nesting_levels.back ();
     587                 :          14 :       xml::printer xp (*current_nesting_level);
     588                 :          14 :       xp.push_tag ("li");
     589                 :          14 :       add_nesting_level_attr (*xp.get_insertion_point (),
     590                 :          14 :                               m_cur_nesting_levels.size ());
     591                 :          14 :       xp.append (std::move (child_diag_element));
     592                 :          14 :       xp.pop_tag ("li");
     593                 :          14 :     }
     594                 :             :   else
     595                 :          15 :     m_cur_diagnostic_element->add_child (std::move (child_diag_element));
     596                 :          29 : }
     597                 :             : 
     598                 :             : void
     599                 :           6 : html_builder::push_nesting_level ()
     600                 :             : {
     601                 :           6 :   gcc_assert (m_cur_diagnostic_element);
     602                 :           6 :   auto new_nesting_level = std::make_unique<xml::element> ("ul", false);
     603                 :          12 :   add_nesting_level_attr (*new_nesting_level,
     604                 :           6 :                           m_cur_nesting_levels.size () + 1);
     605                 :           6 :   xml::element *current_nesting_level = nullptr;
     606                 :           6 :   if (!m_cur_nesting_levels.empty ())
     607                 :           3 :     current_nesting_level = m_cur_nesting_levels.back ();
     608                 :           6 :   m_cur_nesting_levels.push_back (new_nesting_level.get ());
     609                 :           6 :   if (current_nesting_level)
     610                 :           3 :     current_nesting_level->add_child (std::move (new_nesting_level));
     611                 :             :   else
     612                 :           3 :     m_cur_diagnostic_element->add_child (std::move (new_nesting_level));
     613                 :           6 : }
     614                 :             : 
     615                 :             : void
     616                 :           2 : html_builder::pop_nesting_level ()
     617                 :             : {
     618                 :           2 :   gcc_assert (m_cur_diagnostic_element);
     619                 :           2 :   m_cur_nesting_levels.pop_back ();
     620                 :           2 : }
     621                 :             : 
     622                 :             : static void
     623                 :           0 : print_pre_source (xml::printer &xp, const char *text)
     624                 :             : {
     625                 :           0 :   xp.push_tag_with_class ("pre", "source", true);
     626                 :           0 :   xp.add_text (text);
     627                 :           0 :   xp.pop_tag ("pre");
     628                 :           0 : }
     629                 :             : 
     630                 :             : std::unique_ptr<xml::node>
     631                 :          13 : html_builder::maybe_make_state_diagram (const paths::event &event)
     632                 :             : {
     633                 :          13 :   if (!m_html_gen_opts.m_show_state_diagrams)
     634                 :          12 :     return nullptr;
     635                 :             : 
     636                 :           1 :   if (!m_logical_loc_mgr)
     637                 :           0 :     return nullptr;
     638                 :             : 
     639                 :             :   /* Get state graph; if we're going to print it later, also request
     640                 :             :      the debug version.  */
     641                 :           1 :   auto state_graph
     642                 :             :     = event.maybe_make_diagnostic_state_graph
     643                 :           1 :         (m_html_gen_opts.m_show_state_diagrams_sarif);
     644                 :           1 :   if (!state_graph)
     645                 :           0 :     return nullptr;
     646                 :             : 
     647                 :             :   // Convert it to .dot AST
     648                 :           1 :   auto dot_graph = state_graphs::make_dot_graph (*state_graph,
     649                 :           1 :                                                  *m_logical_loc_mgr);
     650                 :           1 :   gcc_assert (dot_graph);
     651                 :             : 
     652                 :           1 :   auto wrapper = std::make_unique<xml::element> ("div", false);
     653                 :           1 :   xml::printer xp (*wrapper);
     654                 :             : 
     655                 :           1 :   if (m_html_gen_opts.m_show_state_diagrams_sarif)
     656                 :             :     {
     657                 :             :       // For debugging, show the SARIF src inline:
     658                 :           0 :       pretty_printer pp;
     659                 :           0 :       state_graph->make_json_sarif_graph ()->print (&pp, true);
     660                 :           0 :       print_pre_source (xp, pp_formatted_text (&pp));
     661                 :           0 :     }
     662                 :             : 
     663                 :           1 :   if (m_html_gen_opts.m_show_state_diagrams_dot_src)
     664                 :             :     {
     665                 :             :       // For debugging, show the dot src inline:
     666                 :           0 :       pretty_printer pp;
     667                 :           0 :       dot::writer w (pp);
     668                 :           0 :       dot_graph->print (w);
     669                 :           0 :       print_pre_source (xp, pp_formatted_text (&pp));
     670                 :           0 :     }
     671                 :             : 
     672                 :             :   // Turn the .dot into SVG and splice into place
     673                 :           1 :   auto svg = dot::make_svg_from_graph (*dot_graph);
     674                 :           1 :   if (svg)
     675                 :           1 :     xp.append (std::move (svg));
     676                 :             : 
     677                 :           1 :   return wrapper;
     678                 :           1 : }
     679                 :             : 
     680                 :             : /* Custom subclass of html_label_writer.
     681                 :             :    Wrap labels within a <span> element, supplying them with event IDs.
     682                 :             :    Add the IDs to the list of focus IDs.  */
     683                 :             : 
     684                 :           3 : class html_path_label_writer : public html_label_writer
     685                 :             : {
     686                 :             : public:
     687                 :           3 :   html_path_label_writer (xml::printer &xp,
     688                 :             :                           html_builder &builder,
     689                 :             :                           const paths::path &path,
     690                 :             :                           const std::string &event_id_prefix)
     691                 :           3 :   : m_xp (xp),
     692                 :           3 :     m_html_builder (builder),
     693                 :           3 :     m_path (path),
     694                 :           3 :     m_event_id_prefix (event_id_prefix),
     695                 :           3 :     m_next_event_idx (0),
     696                 :           3 :     m_curr_event_id ()
     697                 :             :   {
     698                 :             :   }
     699                 :             : 
     700                 :          13 :   void begin_label () final override
     701                 :             :   {
     702                 :          13 :     m_curr_event_id = m_next_event_idx++;
     703                 :          13 :     m_xp.push_tag_with_class ("span", "event", true);
     704                 :          13 :     m_xp.set_attr ("id", get_element_id ());
     705                 :          13 :     m_html_builder.add_focus_id (get_element_id ());
     706                 :          13 :   }
     707                 :             : 
     708                 :          13 :   void end_label () final override
     709                 :             :   {
     710                 :          13 :     const paths::event &event
     711                 :          13 :       = m_path.get_event (m_curr_event_id.zero_based ());
     712                 :          13 :     if (auto state_doc = m_html_builder.maybe_make_state_diagram (event))
     713                 :             :     {
     714                 :           1 :       m_xp.push_tag_with_class ("div", "state-diagram", false);
     715                 :           2 :       m_xp.set_attr ("id", get_element_id () + "-state-diagram");
     716                 :           1 :       m_xp.set_attr ("style",
     717                 :           1 :                      ("position: absolute;"
     718                 :             :                       " z-index: 1;"
     719                 :             :                       " visibility: hidden;"));
     720                 :           1 :       m_xp.append (std::move (state_doc));
     721                 :           1 :       m_xp.pop_tag ("div");
     722                 :          13 :     }
     723                 :             : 
     724                 :          13 :     m_xp.pop_tag ("span"); // from begin_label
     725                 :          13 :   }
     726                 :             : 
     727                 :             : private:
     728                 :             :   std::string
     729                 :          27 :   get_element_id () const
     730                 :             :   {
     731                 :          27 :     gcc_assert (m_curr_event_id.known_p ());
     732                 :          54 :     return (m_event_id_prefix
     733                 :          27 :             + std::to_string (m_curr_event_id.zero_based ()));
     734                 :             :   }
     735                 :             : 
     736                 :             :   xml::printer &m_xp;
     737                 :             :   html_builder &m_html_builder;
     738                 :             :   const paths::path &m_path;
     739                 :             :   const std::string &m_event_id_prefix;
     740                 :             :   int m_next_event_idx;
     741                 :             :   paths::event_id_t m_curr_event_id;
     742                 :             : };
     743                 :             : 
     744                 :             : /* See https://pf3.patternfly.org/v3/pattern-library/widgets/#alerts */
     745                 :             : static const char *
     746                 :          92 : get_pf_class_for_alert_div (enum kind diag_kind)
     747                 :             : {
     748                 :          92 :   switch (diag_kind)
     749                 :             :     {
     750                 :             :     case kind::debug:
     751                 :             :     case kind::note:
     752                 :             :       return "alert alert-info";
     753                 :             : 
     754                 :          65 :     case kind::anachronism:
     755                 :          65 :     case kind::warning:
     756                 :          65 :       return "alert alert-warning";
     757                 :             : 
     758                 :          11 :     case kind::error:
     759                 :          11 :     case kind::sorry:
     760                 :          11 :     case kind::ice:
     761                 :          11 :     case kind::ice_nobt:
     762                 :          11 :     case kind::fatal:
     763                 :          11 :       return "alert alert-danger";
     764                 :             : 
     765                 :           0 :     default:
     766                 :           0 :       gcc_unreachable ();
     767                 :             :     }
     768                 :             : }
     769                 :             : 
     770                 :             : static const char *
     771                 :          92 : get_pf_class_for_alert_icon (enum kind diag_kind)
     772                 :             : {
     773                 :          92 :   switch (diag_kind)
     774                 :             :     {
     775                 :             :     case kind::debug:
     776                 :             :     case kind::note:
     777                 :             :       return "pficon pficon-info";
     778                 :             : 
     779                 :          65 :     case kind::anachronism:
     780                 :          65 :     case kind::warning:
     781                 :          65 :       return "pficon pficon-warning-triangle-o";
     782                 :             : 
     783                 :          11 :     case kind::error:
     784                 :          11 :     case kind::sorry:
     785                 :          11 :     case kind::ice:
     786                 :          11 :     case kind::ice_nobt:
     787                 :          11 :     case kind::fatal:
     788                 :          11 :       return "pficon pficon-error-circle-o";
     789                 :             : 
     790                 :           0 :     default:
     791                 :           0 :       gcc_unreachable ();
     792                 :             :     }
     793                 :             : }
     794                 :             : 
     795                 :             : static const char *
     796                 :          70 : get_label_for_logical_location_kind (enum logical_locations::kind kind)
     797                 :             : {
     798                 :          70 :   switch (kind)
     799                 :             :     {
     800                 :           0 :     default:
     801                 :           0 :       gcc_unreachable ();
     802                 :             :     case logical_locations::kind::unknown:
     803                 :             :       return nullptr;
     804                 :             : 
     805                 :             :     /* Kinds within executable code.  */
     806                 :          70 :     case logical_locations::kind::function:
     807                 :          70 :       return "Function";
     808                 :           0 :     case logical_locations::kind::member:
     809                 :           0 :       return "Member";
     810                 :           0 :     case logical_locations::kind::module_:
     811                 :           0 :       return "Module";
     812                 :           0 :     case logical_locations::kind::namespace_:
     813                 :           0 :       return "Namespace";
     814                 :           0 :     case logical_locations::kind::type:
     815                 :           0 :       return "Type";
     816                 :           0 :     case logical_locations::kind::return_type:
     817                 :           0 :       return "Return type";
     818                 :           0 :     case logical_locations::kind::parameter:
     819                 :           0 :       return "Parameter";
     820                 :           0 :     case logical_locations::kind::variable:
     821                 :           0 :       return "Variable";
     822                 :             : 
     823                 :             :     /* Kinds within XML or HTML documents.  */
     824                 :           0 :     case logical_locations::kind::element:
     825                 :           0 :       return "Element";
     826                 :           0 :     case logical_locations::kind::attribute:
     827                 :           0 :       return "Attribute";
     828                 :           0 :     case logical_locations::kind::text:
     829                 :           0 :       return "Text";
     830                 :           0 :     case logical_locations::kind::comment:
     831                 :           0 :       return "Comment";
     832                 :           0 :     case logical_locations::kind::processing_instruction:
     833                 :           0 :       return "Processing Instruction";
     834                 :           0 :     case logical_locations::kind::dtd:
     835                 :           0 :       return "DTD";
     836                 :           0 :     case logical_locations::kind::declaration:
     837                 :           0 :       return "Declaration";
     838                 :             : 
     839                 :             :   /* Kinds within JSON documents.  */
     840                 :           0 :     case logical_locations::kind::object:
     841                 :           0 :       return "Object";
     842                 :           0 :     case logical_locations::kind::array:
     843                 :           0 :       return "Array";
     844                 :           0 :     case logical_locations::kind::property:
     845                 :           0 :       return "Property";
     846                 :           0 :     case logical_locations::kind::value:
     847                 :           0 :       return "Value";
     848                 :             :     }
     849                 :             : }
     850                 :             : 
     851                 :             : static void
     852                 :         334 : add_labelled_value (xml::printer &xp,
     853                 :             :                     std::string id,
     854                 :             :                     std::string label,
     855                 :             :                     std::string value,
     856                 :             :                     bool quote_value)
     857                 :             : {
     858                 :         334 :   xp.push_tag ("div", true);
     859                 :         668 :   xp.set_attr ("id", id);
     860                 :         334 :   xp.push_tag ("span");
     861                 :         668 :   xp.add_text (label);
     862                 :         334 :   xp.add_text (" ");
     863                 :         334 :   xp.pop_tag ("span");
     864                 :         334 :   xp.push_tag ("span");
     865                 :         334 :   if (quote_value)
     866                 :          70 :     xp.set_attr ("class", "gcc-quoted-text");
     867                 :         334 :   xp.add_text (std::move (value));
     868                 :         334 :   xp.pop_tag ("span");
     869                 :         334 :   xp.pop_tag ("div");
     870                 :         334 : }
     871                 :             : 
     872                 :         106 : class html_token_printer : public token_printer
     873                 :             : {
     874                 :             : public:
     875                 :         118 :   html_token_printer (xml::element &parent_element)
     876                 :             :     /* Ideally pp_token_lists that reach a token_printer should be
     877                 :             :        "balanced", but for now they can have mismatching pp_tokens
     878                 :             :        e.g. a begin_color without an end_color (PR other/120610).
     879                 :             :        Give html_token_printer its own xml::printer as a firewall to
     880                 :             :        limit the scope of the mismatches in the HTML.  */
     881                 :         118 :     : m_xp (parent_element,
     882                 :             :             /* Similarly we don't check that the popped tags match.  */
     883                 :         118 :             false)
     884                 :             :   {
     885                 :             :   }
     886                 :         118 :   void print_tokens (pretty_printer */*pp*/,
     887                 :             :                      const pp_token_list &tokens) final override
     888                 :             :   {
     889                 :             :     /* Implement print_tokens by adding child elements to
     890                 :             :        m_parent_element.  */
     891                 :        1054 :     for (auto iter = tokens.m_first; iter; iter = iter->m_next)
     892                 :         936 :       switch (iter->m_kind)
     893                 :             :         {
     894                 :           0 :         default:
     895                 :           0 :           gcc_unreachable ();
     896                 :             : 
     897                 :         369 :         case pp_token::kind::text:
     898                 :         369 :           {
     899                 :         369 :             pp_token_text *sub = as_a <pp_token_text *> (iter);
     900                 :             :             /* The value might be in the obstack, so we may need to
     901                 :             :                copy it.  */
     902                 :         369 :             m_xp.add_text (sub->m_value.get ());
     903                 :             :             }
     904                 :         369 :           break;
     905                 :             : 
     906                 :         130 :         case pp_token::kind::begin_color:
     907                 :         130 :           {
     908                 :         130 :             pp_token_begin_color *sub = as_a <pp_token_begin_color *> (iter);
     909                 :         130 :             gcc_assert (sub->m_value.get ());
     910                 :         130 :             m_xp.push_tag_with_class ("span", sub->m_value.get ());
     911                 :             :           }
     912                 :         130 :           break;
     913                 :             : 
     914                 :         130 :         case pp_token::kind::end_color:
     915                 :         130 :           m_xp.pop_tag ("span");
     916                 :         130 :           break;
     917                 :             : 
     918                 :         151 :         case pp_token::kind::begin_quote:
     919                 :         151 :           {
     920                 :         151 :             m_xp.add_text (open_quote);
     921                 :         151 :             m_xp.push_tag_with_class ("span", "gcc-quoted-text");
     922                 :             :           }
     923                 :         151 :           break;
     924                 :         151 :         case pp_token::kind::end_quote:
     925                 :         151 :           {
     926                 :         151 :             m_xp.pop_tag ("span");
     927                 :         151 :             m_xp.add_text (close_quote);
     928                 :             :           }
     929                 :         151 :           break;
     930                 :             : 
     931                 :           0 :         case pp_token::kind::begin_url:
     932                 :           0 :           {
     933                 :           0 :             pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);
     934                 :           0 :             m_xp.push_tag ("a", true);
     935                 :           0 :             m_xp.set_attr ("href", sub->m_value.get ());
     936                 :             :           }
     937                 :           0 :           break;
     938                 :           0 :         case pp_token::kind::end_url:
     939                 :           0 :           m_xp.pop_tag ("a");
     940                 :           0 :           break;
     941                 :             : 
     942                 :           5 :         case pp_token::kind::event_id:
     943                 :           5 :           {
     944                 :           5 :             pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
     945                 :           5 :             gcc_assert (sub->m_event_id.known_p ());
     946                 :           5 :             m_xp.add_text ("(");
     947                 :           5 :             m_xp.add_text (std::to_string (sub->m_event_id.one_based ()));
     948                 :           5 :             m_xp.add_text (")");
     949                 :             :           }
     950                 :           5 :           break;
     951                 :             :         }
     952                 :         118 :   }
     953                 :             : 
     954                 :             : private:
     955                 :             :   xml::printer m_xp;
     956                 :             : };
     957                 :             : 
     958                 :             : /* Make a <div class="gcc-diagnostic"> for DIAGNOSTIC.
     959                 :             : 
     960                 :             :    If ALERT is true, make it be a PatternFly alert (see
     961                 :             :    https://pf3.patternfly.org/v3/pattern-library/widgets/#alerts) and
     962                 :             :    show severity text (e.g. "error: ").
     963                 :             : 
     964                 :             :    If ALERT is false, don't show the severity text and don't show
     965                 :             :    the filename if it's the same as the previous diagnostic within the
     966                 :             :    diagnostic group.  */
     967                 :             : 
     968                 :             : std::unique_ptr<xml::element>
     969                 :         106 : html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
     970                 :             :                                            enum kind orig_diag_kind,
     971                 :             :                                            bool alert)
     972                 :             : {
     973                 :         106 :   const int diag_idx = m_next_diag_id++;
     974                 :         106 :   std::string diag_id;
     975                 :         106 :   {
     976                 :         106 :     pretty_printer pp;
     977                 :         106 :     pp_printf (&pp, "gcc-diag-%i", diag_idx);
     978                 :         106 :     diag_id = pp_formatted_text (&pp);
     979                 :         106 :   }
     980                 :             : 
     981                 :             :   // TODO: might be nice to emulate the text output format, but colorize it
     982                 :             : 
     983                 :             :   /* See https://pf3.patternfly.org/v3/pattern-library/widgets/#alerts
     984                 :             :      which has this example:
     985                 :             : <div class="alert alert-danger">
     986                 :             :   <span class="pficon pficon-error-circle-o"></span>
     987                 :             :   <strong>Hey there is a problem!</strong> Yeah this is really messed up and you should <a href="#" class="alert-link">know about it</a>.
     988                 :             : </div>
     989                 :             :   */
     990                 :         106 :   auto diag_element = make_div ("gcc-diagnostic");
     991                 :         212 :   diag_element->set_attr ("id", diag_id);
     992                 :         106 :   if (alert)
     993                 :         184 :     diag_element->set_attr ("class",
     994                 :         184 :                             get_pf_class_for_alert_div (diagnostic.m_kind));
     995                 :             : 
     996                 :         106 :   xml::printer xp (*diag_element.get ());
     997                 :         106 :   const size_t depth_within_alert_div = 1;
     998                 :             : 
     999                 :         106 :   gcc_assert (xp.get_num_open_tags () == depth_within_alert_div);
    1000                 :             : 
    1001                 :         106 :   if (alert)
    1002                 :             :     {
    1003                 :         184 :       xp.push_tag_with_class ("span",
    1004                 :          92 :                               get_pf_class_for_alert_icon (diagnostic.m_kind),
    1005                 :             :                               true);
    1006                 :          92 :       xp.add_text (" ");
    1007                 :          92 :       xp.pop_tag ("span");
    1008                 :             :     }
    1009                 :             : 
    1010                 :             :   // The rest goes in the <div>...
    1011                 :         106 :   gcc_assert (xp.get_num_open_tags () == depth_within_alert_div);
    1012                 :             : 
    1013                 :         106 :   xp.push_tag_with_class ("div", "gcc-message", true);
    1014                 :         106 :   std::string message_alert_id (diag_id + "-message");
    1015                 :         212 :   xp.set_attr ("id", message_alert_id);
    1016                 :         212 :   add_focus_id (message_alert_id);
    1017                 :             : 
    1018                 :         106 :   const size_t depth_within_message_div = depth_within_alert_div + 1;
    1019                 :         106 :   gcc_assert (xp.get_num_open_tags () == depth_within_message_div);
    1020                 :             : 
    1021                 :             :   // Severity e.g. "warning: "
    1022                 :         106 :   bool show_severity = true;
    1023                 :         106 :   if (!alert)
    1024                 :             :     show_severity = false;
    1025                 :          92 :   if (show_severity)
    1026                 :             :   {
    1027                 :          92 :     xp.push_tag ("strong");
    1028                 :          92 :     xp.add_text (_(get_text_for_kind (diagnostic.m_kind)));
    1029                 :          92 :     xp.pop_tag ("strong");
    1030                 :          92 :     xp.add_text (" ");
    1031                 :             :   }
    1032                 :             : 
    1033                 :             :   // Add the message itself:
    1034                 :         106 :   html_token_printer tok_printer (*xp.get_insertion_point ());
    1035                 :         106 :   m_printer->set_token_printer (&tok_printer);
    1036                 :         106 :   pp_output_formatted_text (m_printer, m_context.get_urlifier ());
    1037                 :         106 :   m_printer->set_token_printer (nullptr);
    1038                 :         106 :   pp_clear_output_area (m_printer);
    1039                 :             : 
    1040                 :             :   // Add any metadata as a suffix to the message
    1041                 :         106 :   if (diagnostic.m_metadata)
    1042                 :           4 :     if (auto e = make_element_for_metadata (*diagnostic.m_metadata))
    1043                 :             :       {
    1044                 :           2 :         xp.add_text (" ");
    1045                 :           2 :         xp.append (std::move (e));
    1046                 :           4 :       }
    1047                 :             : 
    1048                 :             :   // Add any option as a suffix to the message
    1049                 :             : 
    1050                 :         106 :   label_text option_text = label_text::take
    1051                 :         106 :     (m_context.make_option_name (diagnostic.m_option_id,
    1052                 :         106 :                                  orig_diag_kind, diagnostic.m_kind));
    1053                 :         106 :   if (option_text.get ())
    1054                 :             :     {
    1055                 :          56 :       label_text option_url = label_text::take
    1056                 :          56 :         (m_context.make_option_url (diagnostic.m_option_id));
    1057                 :             : 
    1058                 :          56 :       xp.add_text (" ");
    1059                 :          56 :       auto option_span = make_span ("gcc-option");
    1060                 :          56 :       option_span->add_text ("[");
    1061                 :          56 :       {
    1062                 :          56 :         if (option_url.get ())
    1063                 :             :           {
    1064                 :          56 :             auto anchor = std::make_unique<xml::element> ("a", true);
    1065                 :          56 :             anchor->set_attr ("href", option_url.get ());
    1066                 :          56 :             anchor->add_text (option_text.get ());
    1067                 :          56 :             option_span->add_child (std::move (anchor));
    1068                 :          56 :           }
    1069                 :             :         else
    1070                 :           0 :           option_span->add_text (option_text.get ());
    1071                 :          56 :         option_span->add_text ("]");
    1072                 :             :       }
    1073                 :          56 :       xp.append (std::move (option_span));
    1074                 :          56 :     }
    1075                 :             : 
    1076                 :         106 :   gcc_assert (xp.get_num_open_tags () == depth_within_message_div);
    1077                 :             : 
    1078                 :         106 :   xp.pop_tag ("div");
    1079                 :             : 
    1080                 :         106 :   gcc_assert (xp.get_num_open_tags () == depth_within_alert_div);
    1081                 :             : 
    1082                 :             :   /* Show any logical location.  */
    1083                 :         106 :   if (m_logical_loc_mgr)
    1084                 :         102 :     if (auto client_data_hooks = m_context.get_client_data_hooks ())
    1085                 :         102 :       if (auto logical_loc = client_data_hooks->get_current_logical_location ())
    1086                 :          99 :         if (logical_loc != m_last_logical_location)
    1087                 :             :           {
    1088                 :          70 :             enum logical_locations::kind kind
    1089                 :          70 :               = m_logical_loc_mgr->get_kind (logical_loc);;
    1090                 :          70 :             if (const char *label = get_label_for_logical_location_kind (kind))
    1091                 :         140 :               if (const char *name_with_scope
    1092                 :          70 :                   = m_logical_loc_mgr->get_name_with_scope (logical_loc))
    1093                 :         140 :                 add_labelled_value (xp, "logical-location",
    1094                 :         140 :                                     label, name_with_scope, true);
    1095                 :          70 :             m_last_logical_location = logical_loc;
    1096                 :             :           }
    1097                 :             : 
    1098                 :             :   /* Show any physical location.  */
    1099                 :         106 :   const expanded_location s
    1100                 :         106 :     = diagnostic_expand_location (&diagnostic);
    1101                 :         106 :   if (s != m_last_expanded_location
    1102                 :         106 :       || alert)
    1103                 :             :     {
    1104                 :          92 :       if (s.file
    1105                 :          88 :           && (s.file != m_last_expanded_location.file
    1106                 :          75 :               || alert))
    1107                 :          88 :         add_labelled_value (xp, "file", "File", s.file, false);
    1108                 :          92 :       if (s.line)
    1109                 :             :         {
    1110                 :          88 :           add_labelled_value (xp, "line", "Line", std::to_string (s.line), false);
    1111                 :          88 :           column_policy col_policy (m_context);
    1112                 :          88 :           int converted_column = col_policy.converted_column (s);
    1113                 :          88 :           if (converted_column >= 0)
    1114                 :          88 :             add_labelled_value (xp, "column", "Column",
    1115                 :         176 :                                 std::to_string (converted_column),
    1116                 :             :                                 false);
    1117                 :             :         }
    1118                 :          92 :       if (s.file)
    1119                 :          88 :         m_last_expanded_location = s;
    1120                 :             :     }
    1121                 :             : 
    1122                 :             :   /* Source (and fix-it hints).  */
    1123                 :         106 :   {
    1124                 :             :     // TODO: m_context.m_last_location should be moved into the sink
    1125                 :         106 :     location_t saved = m_context.m_last_location;
    1126                 :         106 :     m_context.m_last_location = m_last_location;
    1127                 :         106 :     m_context.maybe_show_locus_as_html
    1128                 :         106 :       (*diagnostic.m_richloc,
    1129                 :         106 :        m_context.get_source_printing_options (),
    1130                 :         106 :        diagnostic.m_kind,
    1131                 :             :        xp,
    1132                 :             :        nullptr,
    1133                 :             :        nullptr);
    1134                 :         106 :     m_context.m_last_location = saved;
    1135                 :         106 :     m_last_location = m_context.m_last_location;
    1136                 :             :   }
    1137                 :             : 
    1138                 :         106 :   gcc_assert (xp.get_num_open_tags () == depth_within_alert_div);
    1139                 :             : 
    1140                 :             :   /* Execution path.  */
    1141                 :         106 :   if (auto path = diagnostic.m_richloc->get_path ())
    1142                 :             :     {
    1143                 :           3 :       xp.push_tag ("div");
    1144                 :           3 :       xp.set_attr ("id", "execution-path");
    1145                 :             : 
    1146                 :           3 :       xp.push_tag ("label", true);
    1147                 :           3 :       const int num_events = path->num_events ();
    1148                 :           3 :       pretty_printer pp;
    1149                 :           3 :       pp_printf_n (&pp, num_events,
    1150                 :             :                    "Execution path with %i event",
    1151                 :             :                    "Execution path with %i events",
    1152                 :             :                    num_events);
    1153                 :           3 :       xp.add_text_from_pp (pp);
    1154                 :           3 :       xp.pop_tag ("label");
    1155                 :             : 
    1156                 :           3 :       std::string event_id_prefix (diag_id + "-event-");
    1157                 :           3 :       html_path_label_writer event_label_writer (xp, *this, *path,
    1158                 :           3 :                                                  event_id_prefix);
    1159                 :             : 
    1160                 :           3 :       source_print_policy dspp (m_context);
    1161                 :           3 :       print_path_as_html (xp, *path, m_context, &event_label_writer,
    1162                 :             :                           dspp);
    1163                 :             : 
    1164                 :           3 :       xp.pop_tag ("div");
    1165                 :           3 :     }
    1166                 :             : 
    1167                 :         106 :   gcc_assert (xp.get_num_open_tags () == depth_within_alert_div);
    1168                 :             : 
    1169                 :             :   // Try to display any per-diagnostic graphs
    1170                 :         106 :   if (diagnostic.m_metadata)
    1171                 :           4 :     if (auto ldg = diagnostic.m_metadata->get_lazy_digraphs ())
    1172                 :             :       {
    1173                 :           1 :         auto &digraphs = ldg->get_or_create ();
    1174                 :           3 :         for (auto &dg : digraphs)
    1175                 :           2 :           add_graph (*dg, *xp.get_insertion_point ());
    1176                 :             :       }
    1177                 :             : 
    1178                 :         106 :   if (auto patch_element = make_element_for_patch (diagnostic))
    1179                 :             :     {
    1180                 :          34 :       xp.push_tag ("div");
    1181                 :          34 :       xp.set_attr ("id", "suggested-fix");
    1182                 :          34 :       xp.push_tag ("label", true);
    1183                 :          34 :       xp.add_text ("Suggested fix");
    1184                 :          34 :       xp.pop_tag ("label");
    1185                 :          34 :       xp.append (std::move (patch_element));
    1186                 :          34 :       xp.pop_tag ("div");
    1187                 :         106 :     }
    1188                 :             : 
    1189                 :         106 :   return diag_element;
    1190                 :         106 : }
    1191                 :             : 
    1192                 :             : std::unique_ptr<xml::element>
    1193                 :         106 : html_builder::make_element_for_patch (const diagnostic_info &diagnostic)
    1194                 :             : {
    1195                 :         106 :   changes::change_set edit (m_context.get_file_cache ());
    1196                 :         106 :   edit.add_fixits (diagnostic.m_richloc);
    1197                 :         106 :   if (char *diff = edit.generate_diff (true))
    1198                 :             :     {
    1199                 :         106 :       if (strlen (diff) > 0)
    1200                 :             :         {
    1201                 :          34 :           auto element = std::make_unique<xml::element> ("pre", true);
    1202                 :          34 :           element->set_attr ("class", "gcc-generated-patch");
    1203                 :          34 :           element->add_text (diff);
    1204                 :          34 :           free (diff);
    1205                 :          34 :           return element;
    1206                 :             :         }
    1207                 :             :       else
    1208                 :          72 :         free (diff);
    1209                 :             :     }
    1210                 :          72 :   return nullptr;
    1211                 :         106 : }
    1212                 :             : 
    1213                 :             : std::unique_ptr<xml::element>
    1214                 :          11 : html_builder::make_metadata_element (label_text label,
    1215                 :             :                                      label_text url)
    1216                 :             : {
    1217                 :          11 :   auto item = make_span ("gcc-metadata-item");
    1218                 :          11 :   xml::printer xp (*item.get ());
    1219                 :          11 :   xp.add_text ("[");
    1220                 :          11 :   {
    1221                 :          11 :     if (url.get ())
    1222                 :             :       {
    1223                 :          11 :         xp.push_tag ("a", true);
    1224                 :          11 :         xp.set_attr ("href", url.get ());
    1225                 :             :       }
    1226                 :          11 :     xp.add_text (label.get ());
    1227                 :          11 :     if (url.get ())
    1228                 :          11 :       xp.pop_tag ("a");
    1229                 :             :   }
    1230                 :          11 :   xp.add_text ("]");
    1231                 :          11 :   return item;
    1232                 :          11 : }
    1233                 :             : 
    1234                 :             : std::unique_ptr<xml::element>
    1235                 :          12 : html_builder::make_element_for_metadata (const metadata &m)
    1236                 :             : {
    1237                 :          12 :   auto span_metadata = make_span ("gcc-metadata");
    1238                 :             : 
    1239                 :          12 :   int cwe = m.get_cwe ();
    1240                 :          12 :   if (cwe)
    1241                 :             :     {
    1242                 :           6 :       pretty_printer pp;
    1243                 :           6 :       pp_printf (&pp, "CWE-%i", cwe);
    1244                 :           6 :       label_text label = label_text::take (xstrdup (pp_formatted_text (&pp)));
    1245                 :           6 :       label_text url = label_text::take (get_cwe_url (cwe));
    1246                 :           6 :       span_metadata->add_child
    1247                 :          18 :         (make_metadata_element (std::move (label), std::move (url)));
    1248                 :           6 :     }
    1249                 :             : 
    1250                 :          17 :   for (unsigned idx = 0; idx < m.get_num_rules (); ++idx)
    1251                 :             :     {
    1252                 :           5 :       auto &rule = m.get_rule (idx);
    1253                 :           5 :       label_text label = label_text::take (rule.make_description ());
    1254                 :           5 :       label_text url = label_text::take (rule.make_url ());
    1255                 :           5 :       span_metadata->add_child
    1256                 :          15 :         (make_metadata_element (std::move (label), std::move (url)));
    1257                 :           5 :     }
    1258                 :             : 
    1259                 :          12 :   if (span_metadata->m_children.empty ())
    1260                 :           2 :     return nullptr;
    1261                 :             : 
    1262                 :          10 :   return span_metadata;
    1263                 :          12 : }
    1264                 :             : 
    1265                 :             : /* Implementation of diagnostics::context::m_diagrams.m_emission_cb
    1266                 :             :    for HTML output.  */
    1267                 :             : 
    1268                 :             : void
    1269                 :           0 : html_builder::emit_diagram (const diagram &)
    1270                 :             : {
    1271                 :             :   /* We must be within the emission of a top-level diagnostic.  */
    1272                 :           0 :   gcc_assert (m_cur_diagnostic_element);
    1273                 :             : 
    1274                 :             :   // TODO: currently a no-op
    1275                 :           0 : }
    1276                 :             : 
    1277                 :             : void
    1278                 :           3 : html_builder::add_graph (const digraphs::digraph &dg,
    1279                 :             :                          xml::element &parent_element)
    1280                 :             : {
    1281                 :           3 :   if (auto dot_graph = dg.make_dot_graph ())
    1282                 :           3 :     if (auto svg_element = dot::make_svg_from_graph (*dot_graph))
    1283                 :             :       {
    1284                 :           3 :         auto div = std::make_unique<xml::element> ("div", false);
    1285                 :           3 :         div->set_attr ("class", "gcc-directed-graph");
    1286                 :           3 :         xml::printer xp (*div);
    1287                 :           3 :         if (const char *description = dg.get_description ())
    1288                 :             :           {
    1289                 :           3 :             xp.push_tag ("h2", true);
    1290                 :           3 :             xp.add_text (description);
    1291                 :           3 :             xp.pop_tag ("h2");
    1292                 :             :           }
    1293                 :           3 :         xp.append (std::move (svg_element));
    1294                 :           3 :         parent_element.add_child (std::move (div));
    1295                 :           9 :       }
    1296                 :           3 : }
    1297                 :             : 
    1298                 :             : void
    1299                 :           1 : html_builder::emit_global_graph (const lazily_created<digraphs::digraph> &ldg)
    1300                 :             : {
    1301                 :           1 :   auto &dg = ldg.get_or_create ();
    1302                 :           1 :   gcc_assert (m_body_element);
    1303                 :           1 :   add_graph (dg, *m_body_element);
    1304                 :           1 : }
    1305                 :             : 
    1306                 :             : /* Implementation of "end_group_cb" for HTML output.  */
    1307                 :             : 
    1308                 :             : void
    1309                 :          77 : html_builder::end_group ()
    1310                 :             : {
    1311                 :          77 :   if (m_cur_diagnostic_element)
    1312                 :          77 :     m_diagnostics_element->add_child (std::move (m_cur_diagnostic_element));
    1313                 :          77 : }
    1314                 :             : 
    1315                 :             : /* Create a top-level object, and add it to all the results
    1316                 :             :    (and other entities) we've seen so far.
    1317                 :             : 
    1318                 :             :    Flush it all to OUTF.  */
    1319                 :             : 
    1320                 :             : void
    1321                 :          13 : html_builder::flush_to_file (FILE *outf)
    1322                 :             : {
    1323                 :          13 :   DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_context.get_logger (),
    1324                 :          13 :                                  "diagnostics::html_builder::flush_to_file");
    1325                 :          13 :   if (m_html_gen_opts.m_javascript)
    1326                 :             :     {
    1327                 :           0 :       gcc_assert (m_head_element);
    1328                 :           0 :       xml::printer xp (*m_head_element);
    1329                 :             :       /* Add an initialization of the global js variable "focus_ids"
    1330                 :             :          using the array of IDs we saved as we went.  */
    1331                 :           0 :       xp.push_tag ("script");
    1332                 :           0 :       pretty_printer pp;
    1333                 :           0 :       pp_string (&pp, "focus_ids = ");
    1334                 :           0 :       m_ui_focus_ids.print (&pp, true);
    1335                 :           0 :       pp_string (&pp, ";\n");
    1336                 :           0 :       xp.add_raw (pp_formatted_text (&pp));
    1337                 :           0 :       xp.pop_tag ("script");
    1338                 :           0 :     }
    1339                 :          13 :   auto top = m_document.get ();
    1340                 :          13 :   top->dump (outf);
    1341                 :          13 :   fprintf (outf, "\n");
    1342                 :          13 : }
    1343                 :             : 
    1344                 :             : class html_sink : public sink
    1345                 :             : {
    1346                 :             : public:
    1347                 :          21 :   ~html_sink ()
    1348                 :          21 :   {
    1349                 :             :     /* Any diagnostics should have been handled by now.
    1350                 :             :        If not, then something's gone wrong with diagnostic
    1351                 :             :        groupings.  */
    1352                 :          21 :     std::unique_ptr<xml::element> pending_diag
    1353                 :          21 :       = m_builder.take_current_diagnostic ();
    1354                 :          21 :     gcc_assert (!pending_diag);
    1355                 :          21 :   }
    1356                 :             : 
    1357                 :           0 :   void dump (FILE *out, int indent) const override
    1358                 :             :   {
    1359                 :           0 :     sink::dump (out, indent);
    1360                 :           0 :     dumping::emit_heading (out, indent, "html_builder");
    1361                 :           0 :     m_builder.dump (out, indent + 2);
    1362                 :           0 :   }
    1363                 :             : 
    1364                 :             :   void
    1365                 :          21 :   set_main_input_filename (const char *name) final override
    1366                 :             :   {
    1367                 :          21 :     m_builder.set_main_input_filename (name);
    1368                 :          13 :   }
    1369                 :             : 
    1370                 :             :   std::unique_ptr<per_sink_buffer>
    1371                 :           0 :   make_per_sink_buffer () final override
    1372                 :             :   {
    1373                 :           0 :     return std::make_unique<html_sink_buffer> (m_builder);
    1374                 :             :   }
    1375                 :           0 :   void set_buffer (per_sink_buffer *base_buffer) final override
    1376                 :             :   {
    1377                 :           0 :     html_sink_buffer *buffer
    1378                 :             :       = static_cast<html_sink_buffer *> (base_buffer);
    1379                 :           0 :     m_buffer = buffer;
    1380                 :           0 :   }
    1381                 :             : 
    1382                 :          77 :   void on_begin_group () final override
    1383                 :             :   {
    1384                 :             :     /* No-op,  */
    1385                 :          77 :   }
    1386                 :          77 :   void on_end_group () final override
    1387                 :             :   {
    1388                 :          77 :     m_builder.end_group ();
    1389                 :          77 :   }
    1390                 :             :   void
    1391                 :         106 :   on_report_diagnostic (const diagnostic_info &diagnostic,
    1392                 :             :                         enum kind orig_diag_kind) final override
    1393                 :             :   {
    1394                 :         106 :     DIAGNOSTICS_LOG_SCOPE_PRINTF0
    1395                 :             :       (get_logger (),
    1396                 :         106 :        "diagnostics::html_sink::on_report_diagnostic");
    1397                 :         106 :     m_builder.on_report_diagnostic (diagnostic, orig_diag_kind, m_buffer);
    1398                 :         106 :   }
    1399                 :           0 :   void on_diagram (const diagram &d) final override
    1400                 :             :   {
    1401                 :           0 :     m_builder.emit_diagram (d);
    1402                 :           0 :   }
    1403                 :         103 :   void after_diagnostic (const diagnostic_info &) final override
    1404                 :             :   {
    1405                 :             :     /* No-op, but perhaps could show paths here.  */
    1406                 :         103 :   }
    1407                 :           0 :   bool follows_reference_printer_p () const final override
    1408                 :             :   {
    1409                 :           0 :     return false;
    1410                 :             :   }
    1411                 :          21 :   void update_printer () final override
    1412                 :             :   {
    1413                 :          21 :     m_printer = m_context.clone_printer ();
    1414                 :             : 
    1415                 :             :     /* Don't colorize the text.  */
    1416                 :          21 :     pp_show_color (m_printer.get ()) = false;
    1417                 :             : 
    1418                 :             :     /* No textual URLs.  */
    1419                 :          21 :     m_printer->set_url_format (URL_FORMAT_NONE);
    1420                 :             : 
    1421                 :             :     /* Update the builder to use the new printer.  */
    1422                 :          21 :     m_builder.set_printer (*get_printer ());
    1423                 :          21 :   }
    1424                 :             : 
    1425                 :             :   void
    1426                 :           1 :   report_global_digraph (const lazily_created<digraphs::digraph> &ldg)
    1427                 :             :     final override
    1428                 :             :   {
    1429                 :           1 :     m_builder.emit_global_graph (ldg);
    1430                 :           1 :   }
    1431                 :             : 
    1432                 :           4 :   const xml::document &get_document () const
    1433                 :             :   {
    1434                 :           4 :     return m_builder.get_document ();
    1435                 :             :   }
    1436                 :             : 
    1437                 :           4 :   html_builder &get_builder () { return m_builder; }
    1438                 :             : 
    1439                 :             : protected:
    1440                 :          21 :   html_sink (context &dc,
    1441                 :             :              const line_maps *line_maps,
    1442                 :             :              const html_generation_options &html_gen_opts)
    1443                 :          21 :   : sink (dc),
    1444                 :          21 :     m_builder (dc, *get_printer (), line_maps, html_gen_opts),
    1445                 :          21 :     m_buffer (nullptr)
    1446                 :          21 :   {}
    1447                 :             : 
    1448                 :             :   html_builder m_builder;
    1449                 :             :   html_sink_buffer *m_buffer;
    1450                 :             : };
    1451                 :             : 
    1452                 :             : class html_file_sink : public html_sink
    1453                 :             : {
    1454                 :             : public:
    1455                 :          13 :   html_file_sink (context &dc,
    1456                 :             :                   const line_maps *line_maps,
    1457                 :             :                   const html_generation_options &html_gen_opts,
    1458                 :             :                   output_file output_file_)
    1459                 :          13 :   : html_sink (dc, line_maps, html_gen_opts),
    1460                 :          13 :     m_output_file (std::move (output_file_))
    1461                 :             :   {
    1462                 :          13 :     gcc_assert (m_output_file.get_open_file ());
    1463                 :          13 :     gcc_assert (m_output_file.get_filename ());
    1464                 :          13 :   }
    1465                 :          26 :   ~html_file_sink ()
    1466                 :          13 :   {
    1467                 :          13 :     m_builder.flush_to_file (m_output_file.get_open_file ());
    1468                 :          26 :   }
    1469                 :           0 :   void dump_kind (FILE *out) const override
    1470                 :             :   {
    1471                 :           0 :     fprintf (out, "html_file_sink: %s",
    1472                 :             :              m_output_file.get_filename ());
    1473                 :           0 :   }
    1474                 :           7 :   bool machine_readable_stderr_p () const final override
    1475                 :             :   {
    1476                 :           7 :     return false;
    1477                 :             :   }
    1478                 :             : 
    1479                 :             : private:
    1480                 :             :   output_file m_output_file;
    1481                 :             : };
    1482                 :             : 
    1483                 :             : /* Attempt to open BASE_FILE_NAME.html for writing.
    1484                 :             :    Return a non-null output_file,
    1485                 :             :    or return a null output_file and complain to DC
    1486                 :             :    using LINE_MAPS.  */
    1487                 :             : 
    1488                 :             : output_file
    1489                 :          13 : open_html_output_file (context &dc,
    1490                 :             :                        line_maps *line_maps,
    1491                 :             :                        const char *base_file_name)
    1492                 :             : {
    1493                 :          13 :   if (!base_file_name)
    1494                 :             :     {
    1495                 :           0 :       rich_location richloc (line_maps, UNKNOWN_LOCATION);
    1496                 :           0 :       dc.emit_diagnostic_with_group
    1497                 :           0 :         (kind::error, richloc, nullptr, 0,
    1498                 :             :          "unable to determine filename for HTML output");
    1499                 :           0 :       return output_file ();
    1500                 :           0 :     }
    1501                 :             : 
    1502                 :          13 :   label_text filename = label_text::take (concat (base_file_name,
    1503                 :             :                                                   ".html",
    1504                 :          13 :                                                   nullptr));
    1505                 :          13 :   FILE *outf = fopen (filename.get (), "w");
    1506                 :          13 :   if (!outf)
    1507                 :             :     {
    1508                 :           0 :       rich_location richloc (line_maps, UNKNOWN_LOCATION);
    1509                 :           0 :       dc.emit_diagnostic_with_group
    1510                 :           0 :         (kind::error, richloc, nullptr, 0,
    1511                 :             :          "unable to open %qs for HTML output: %m",
    1512                 :             :          filename.get ());
    1513                 :           0 :       return output_file ();
    1514                 :           0 :     }
    1515                 :          13 :   return output_file (outf, true, std::move (filename));
    1516                 :          13 : }
    1517                 :             : 
    1518                 :             : std::unique_ptr<sink>
    1519                 :          13 : make_html_sink (context &dc,
    1520                 :             :                 const line_maps &line_maps,
    1521                 :             :                 const html_generation_options &html_gen_opts,
    1522                 :             :                 output_file output_file_)
    1523                 :             : {
    1524                 :          13 :   auto sink
    1525                 :             :     = std::make_unique<html_file_sink> (dc,
    1526                 :          26 :                                         &line_maps,
    1527                 :             :                                         html_gen_opts,
    1528                 :          13 :                                         std::move (output_file_));
    1529                 :          13 :   sink->update_printer ();
    1530                 :          13 :   return sink;
    1531                 :          13 : }
    1532                 :             : 
    1533                 :             : #if CHECKING_P
    1534                 :             : 
    1535                 :             : namespace selftest {
    1536                 :             : 
    1537                 :             : /* Helper for writing tests of html_token_printer.
    1538                 :             :    Printing to m_pp will appear as HTML within m_top_element, a <div>.  */
    1539                 :             : 
    1540                 :             : struct token_printer_test
    1541                 :             : {
    1542                 :          12 :   token_printer_test ()
    1543                 :          12 :   : m_top_element ("div", true),
    1544                 :          12 :     m_tok_printer (m_top_element)
    1545                 :             :   {
    1546                 :          12 :     m_pp.set_token_printer (&m_tok_printer);
    1547                 :          12 :   }
    1548                 :             : 
    1549                 :             :   xml::element m_top_element;
    1550                 :             :   html_token_printer m_tok_printer;
    1551                 :             :   pretty_printer m_pp;
    1552                 :             : };
    1553                 :             : 
    1554                 :             : static void
    1555                 :           4 : test_token_printer ()
    1556                 :             : {
    1557                 :           4 :   {
    1558                 :           4 :     token_printer_test t;
    1559                 :           4 :     pp_printf (&t.m_pp, "hello world");
    1560                 :           4 :     ASSERT_XML_PRINT_EQ
    1561                 :             :       (t.m_top_element,
    1562                 :             :        "<div>hello world</div>\n");
    1563                 :           4 :   }
    1564                 :             : 
    1565                 :           4 :   {
    1566                 :           4 :     token_printer_test t;
    1567                 :           4 :     pp_printf (&t.m_pp, "%qs: %qs", "foo", "bar");
    1568                 :           4 :     ASSERT_XML_PRINT_EQ
    1569                 :             :       (t.m_top_element,
    1570                 :             :        "<div>"
    1571                 :             :        "`"
    1572                 :             :        "<span class=\"gcc-quoted-text\">"
    1573                 :             :        "foo"
    1574                 :             :        "</span>"
    1575                 :             :        "&apos;: `"
    1576                 :             :        "<span class=\"gcc-quoted-text\">"
    1577                 :             :        "bar"
    1578                 :             :        "</span>"
    1579                 :             :        "&apos;"
    1580                 :             :        "</div>\n");
    1581                 :           4 :   }
    1582                 :             : 
    1583                 :           4 :   {
    1584                 :           4 :     token_printer_test t;
    1585                 :           4 :     paths::event_id_t event_id (0);
    1586                 :           4 :     pp_printf (&t.m_pp, "foo %@ bar", &event_id);
    1587                 :           4 :     ASSERT_XML_PRINT_EQ
    1588                 :             :       (t.m_top_element,
    1589                 :             :        "<div>foo (1) bar</div>\n");
    1590                 :           4 :   }
    1591                 :           4 : }
    1592                 :             : 
    1593                 :             : /* A subclass of html_sink for writing selftests.
    1594                 :             :    The XML output is cached internally, rather than written
    1595                 :             :    out to a file.  */
    1596                 :             : 
    1597                 :          16 : class test_html_context : public test_context
    1598                 :             : {
    1599                 :             : public:
    1600                 :           8 :   test_html_context ()
    1601                 :           8 :   {
    1602                 :           8 :     html_generation_options html_gen_opts;
    1603                 :           8 :     html_gen_opts.m_css = false;
    1604                 :           8 :     html_gen_opts.m_javascript = false;
    1605                 :           8 :     auto sink = std::make_unique<html_buffered_sink> (*this,
    1606                 :             :                                                       line_table,
    1607                 :           8 :                                                       html_gen_opts);
    1608                 :           8 :     sink->update_printer ();
    1609                 :           8 :     sink->set_main_input_filename ("(main input filename)");
    1610                 :           8 :     m_format = sink.get (); // borrowed
    1611                 :             : 
    1612                 :           8 :     set_sink (std::move (sink));
    1613                 :           8 :   }
    1614                 :             : 
    1615                 :           4 :   const xml::document &get_document () const
    1616                 :             :   {
    1617                 :           4 :     return m_format->get_document ();
    1618                 :             :   }
    1619                 :             : 
    1620                 :           4 :   html_builder &get_builder () const
    1621                 :             :   {
    1622                 :           4 :     return m_format->get_builder ();
    1623                 :             :   }
    1624                 :             : 
    1625                 :             : private:
    1626                 :             :   class html_buffered_sink : public html_sink
    1627                 :             :   {
    1628                 :             :   public:
    1629                 :           8 :     html_buffered_sink (context &dc,
    1630                 :             :                         const line_maps *line_maps,
    1631                 :             :                         const html_generation_options &html_gen_opts)
    1632                 :           8 :     : html_sink (dc, line_maps, html_gen_opts)
    1633                 :             :     {
    1634                 :             :     }
    1635                 :           0 :     void dump_kind (FILE *out) const final override
    1636                 :             :     {
    1637                 :           0 :       fprintf (out, "html_buffered_sink");
    1638                 :           0 :     }
    1639                 :           0 :     bool machine_readable_stderr_p () const final override
    1640                 :             :     {
    1641                 :           0 :       return true;
    1642                 :             :     }
    1643                 :             :   };
    1644                 :             : 
    1645                 :             :   html_sink *m_format; // borrowed
    1646                 :             : };
    1647                 :             : 
    1648                 :             : /* Test of reporting a diagnostic at UNKNOWN_LOCATION to a
    1649                 :             :    diagnostics::context and examining the generated XML document.
    1650                 :             :    Verify various basic properties. */
    1651                 :             : 
    1652                 :             : static void
    1653                 :           4 : test_simple_log ()
    1654                 :             : {
    1655                 :           4 :   test_html_context dc;
    1656                 :             : 
    1657                 :           4 :   rich_location richloc (line_table, UNKNOWN_LOCATION);
    1658                 :           4 :   dc.report (kind::error, richloc, nullptr, 0, "this is a test: %qs", "foo");
    1659                 :             : 
    1660                 :           4 :   const xml::document &doc  = dc.get_document ();
    1661                 :             : 
    1662                 :           4 :   ASSERT_XML_PRINT_EQ
    1663                 :             :     (doc,
    1664                 :             :      ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    1665                 :             :       "<!DOCTYPE html\n"
    1666                 :             :       "     PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n"
    1667                 :             :       "     \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
    1668                 :             :       "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
    1669                 :             :       "  <head>\n"
    1670                 :             :       "    <title>(main input filename)</title>\n"
    1671                 :             :       "  </head>\n"
    1672                 :             :       "  <body>\n"
    1673                 :             :       "    <div class=\"gcc-diagnostic-list\">\n"
    1674                 :             :       "      <div class=\"alert alert-danger\" id=\"gcc-diag-0\">\n"
    1675                 :             :       "        <span class=\"pficon pficon-error-circle-o\"> </span>\n"
    1676                 :             :       "        <div class=\"gcc-message\" id=\"gcc-diag-0-message\"><strong>error: </strong> this is a test: `<span class=\"gcc-quoted-text\">foo</span>&apos;</div>\n"
    1677                 :             :       "      </div>\n"
    1678                 :             :       "    </div>\n"
    1679                 :             :       "  </body>\n"
    1680                 :             :       "</html>\n"));
    1681                 :           4 : }
    1682                 :             : 
    1683                 :             : static void
    1684                 :           4 : test_metadata ()
    1685                 :             : {
    1686                 :           4 :   test_html_context dc;
    1687                 :           4 :   html_builder &b = dc.get_builder ();
    1688                 :             : 
    1689                 :           4 :   {
    1690                 :           4 :     metadata m;
    1691                 :           4 :     m.add_cwe (415);
    1692                 :           4 :     auto element = b.make_element_for_metadata (m);
    1693                 :           4 :     ASSERT_XML_PRINT_EQ
    1694                 :             :       (*element,
    1695                 :             :        "<span class=\"gcc-metadata\">"
    1696                 :             :        "<span class=\"gcc-metadata-item\">"
    1697                 :             :        "["
    1698                 :             :        "<a href=\"https://cwe.mitre.org/data/definitions/415.html\">"
    1699                 :             :        "CWE-415"
    1700                 :             :        "</a>"
    1701                 :             :        "]"
    1702                 :             :        "</span>"
    1703                 :             :        "</span>\n");
    1704                 :           4 :   }
    1705                 :             : 
    1706                 :           4 :   {
    1707                 :           4 :     metadata m;
    1708                 :           4 :     metadata::precanned_rule rule ("MISC-42",
    1709                 :           4 :                                    "http://example.com");
    1710                 :           4 :     m.add_rule (rule);
    1711                 :           4 :     auto element = b.make_element_for_metadata (m);
    1712                 :           4 :     ASSERT_XML_PRINT_EQ
    1713                 :             :       (*element,
    1714                 :             :        "<span class=\"gcc-metadata\">"
    1715                 :             :        "<span class=\"gcc-metadata-item\">"
    1716                 :             :        "["
    1717                 :             :        "<a href=\"http://example.com\">"
    1718                 :             :        "MISC-42"
    1719                 :             :        "</a>"
    1720                 :             :        "]"
    1721                 :             :        "</span>"
    1722                 :             :        "</span>\n");
    1723                 :           4 :   }
    1724                 :           4 : }
    1725                 :             : 
    1726                 :             : /* Run all of the selftests within this file.  */
    1727                 :             : 
    1728                 :             : void
    1729                 :           4 : html_sink_cc_tests ()
    1730                 :             : {
    1731                 :           4 :   ::selftest::auto_fix_quotes fix_quotes;
    1732                 :           4 :   test_token_printer ();
    1733                 :           4 :   test_simple_log ();
    1734                 :           4 :   test_metadata ();
    1735                 :           4 : }
    1736                 :             : 
    1737                 :             : } // namespace selftest
    1738                 :             : 
    1739                 :             : #endif /* CHECKING_P */
    1740                 :             : 
    1741                 :             : } // 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.