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

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.