LCOV - code coverage report
Current view: top level - gcc - xml.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 92.0 % 237 218
Test Date: 2025-08-30 13:27:53 Functions: 96.9 % 32 31
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* XML support 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 "xml.h"
      28                 :             : #include "xml-printer.h"
      29                 :             : #include "pretty-print.h"
      30                 :             : #include "selftest.h"
      31                 :             : #include "selftest-xml.h"
      32                 :             : 
      33                 :             : namespace xml {
      34                 :             : 
      35                 :             : /* Disable warnings about quoting issues in the pp_xxx calls below
      36                 :             :    that (intentionally) don't follow GCC diagnostic conventions.  */
      37                 :             : #if __GNUC__ >= 10
      38                 :             : #  pragma GCC diagnostic push
      39                 :             : #  pragma GCC diagnostic ignored "-Wformat-diag"
      40                 :             : #endif
      41                 :             : 
      42                 :             : 
      43                 :             : /* Implementation.  */
      44                 :             : 
      45                 :             : static void
      46                 :       10205 : write_escaped_text (pretty_printer *pp, const char *text)
      47                 :             : {
      48                 :       10205 :   gcc_assert (text);
      49                 :             : 
      50                 :      140394 :   for (const char *p = text; *p; ++p)
      51                 :             :     {
      52                 :      130189 :       char ch = *p;
      53                 :      130189 :       switch (ch)
      54                 :             :         {
      55                 :      128559 :         default:
      56                 :      128559 :           pp_character (pp, ch);
      57                 :      128559 :           break;
      58                 :         798 :         case '\'':
      59                 :         798 :           pp_string (pp, "&apos;");
      60                 :         798 :           break;
      61                 :         830 :         case '"':
      62                 :         830 :           pp_string (pp, "&quot;");
      63                 :         830 :           break;
      64                 :           0 :         case '&':
      65                 :           0 :           pp_string (pp, "&amp;");
      66                 :           0 :           break;
      67                 :           2 :         case '<':
      68                 :           2 :           pp_string (pp, "&lt;");
      69                 :           2 :           break;
      70                 :           0 :         case '>':
      71                 :           0 :           pp_string (pp, "&gt;");
      72                 :           0 :           break;
      73                 :             :         }
      74                 :             :     }
      75                 :       10205 : }
      76                 :             : 
      77                 :             : /* struct node.  */
      78                 :             : 
      79                 :             : void
      80                 :          10 : node::dump (FILE *out) const
      81                 :             : {
      82                 :          10 :   pretty_printer pp;
      83                 :          10 :   pp.set_output_stream (out);
      84                 :          10 :   write_as_xml (&pp, 0, true);
      85                 :          10 :   pp_flush (&pp);
      86                 :          10 : }
      87                 :             : 
      88                 :             : /* struct text : public node.  */
      89                 :             : 
      90                 :             : void
      91                 :        4868 : text::write_as_xml (pretty_printer *pp, int depth, bool indent) const
      92                 :             : {
      93                 :        4868 :   if (indent)
      94                 :             :     {
      95                 :          28 :       for (int i = 0; i < depth; ++i)
      96                 :          20 :         pp_string (pp, "  ");
      97                 :             :     }
      98                 :        4868 :   write_escaped_text (pp, m_str.c_str ());
      99                 :        4868 :   if (indent)
     100                 :           8 :     pp_newline (pp);
     101                 :        4868 : }
     102                 :             : 
     103                 :             : /* struct node_with_children : public node.  */
     104                 :             : 
     105                 :             : void
     106                 :       11779 : node_with_children::add_child (std::unique_ptr<node> node)
     107                 :             : {
     108                 :       11779 :   gcc_assert (node.get ());
     109                 :       11779 :   m_children.push_back (std::move (node));
     110                 :       11779 : }
     111                 :             : 
     112                 :             : void
     113                 :       30493 : node_with_children::add_text (std::string str)
     114                 :             : {
     115                 :             :   // Consolidate runs of text
     116                 :       30493 :   if (!m_children.empty ())
     117                 :       26112 :     if (text *t = m_children.back ()->dyn_cast_text ())
     118                 :             :       {
     119                 :       25603 :         t->m_str += std::move (str);
     120                 :       25603 :         return;
     121                 :             :       }
     122                 :        4890 :   add_child (std::make_unique <text> (std::move (str)));
     123                 :             : }
     124                 :             : 
     125                 :             : void
     126                 :        7399 : node_with_children::add_text_from_pp (pretty_printer &pp)
     127                 :             : {
     128                 :        7399 :   add_text (pp_formatted_text (&pp));
     129                 :        7399 : }
     130                 :             : 
     131                 :             : void
     132                 :           8 : node_with_children::add_comment (std::string str)
     133                 :             : {
     134                 :           8 :   add_child (std::make_unique <comment> (std::move (str)));
     135                 :           8 : }
     136                 :             : 
     137                 :             : element *
     138                 :          12 : node_with_children::find_child_element (std::string kind) const
     139                 :             : {
     140                 :          20 :   for (auto &iter : m_children)
     141                 :          16 :     if (element *e = iter->dyn_cast_element ())
     142                 :          12 :       if (e->m_kind == kind)
     143                 :          12 :         return e;
     144                 :             :   return nullptr;
     145                 :             : }
     146                 :             : 
     147                 :             : /* struct document : public node_with_children.  */
     148                 :             : 
     149                 :             : void
     150                 :          22 : document::write_as_xml (pretty_printer *pp, int depth, bool indent) const
     151                 :             : {
     152                 :          22 :   pp_string (pp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
     153                 :          22 :   if (m_doctypedecl)
     154                 :          14 :     m_doctypedecl->write_as_xml (pp, depth, indent);
     155                 :          44 :   for (auto &iter : m_children)
     156                 :          22 :     iter->write_as_xml (pp, depth, indent);
     157                 :          22 : }
     158                 :             : 
     159                 :             : /* struct element : public node_with_children.  */
     160                 :             : 
     161                 :             : void
     162                 :        6884 : element::write_as_xml (pretty_printer *pp, int depth, bool indent) const
     163                 :             : {
     164                 :        6884 :   if (indent)
     165                 :             :     {
     166                 :       11926 :       for (int i = 0; i < depth; ++i)
     167                 :        9316 :         pp_string (pp, "  ");
     168                 :             :     }
     169                 :             : 
     170                 :        6884 :   pp_printf (pp, "<%s", m_kind.c_str ());
     171                 :       12213 :   for (auto &key : m_key_insertion_order)
     172                 :             :     {
     173                 :        5329 :       auto iter = m_attributes.find (key);
     174                 :        5329 :       if (iter != m_attributes.end ())
     175                 :             :         {
     176                 :        5329 :           pp_printf (pp, " %s=\"", key.c_str ());
     177                 :        5329 :           write_escaped_text (pp, iter->second.c_str ());
     178                 :        5329 :           pp_string (pp, "\"");
     179                 :             :         }
     180                 :             :     }
     181                 :        6884 :   if (m_children.empty ())
     182                 :          43 :     pp_string (pp, "/>");
     183                 :             :   else
     184                 :             :     {
     185                 :        6841 :       const bool indent_children = m_preserve_whitespace ? false : indent;
     186                 :        6841 :       pp_string (pp, ">");
     187                 :        6841 :       if (indent_children)
     188                 :         861 :         pp_newline (pp);
     189                 :       18364 :       for (auto &child : m_children)
     190                 :       11523 :         child->write_as_xml (pp, depth + 1, indent_children);
     191                 :        6841 :       if (indent_children)
     192                 :             :         {
     193                 :        3114 :           for (int i = 0; i < depth; ++i)
     194                 :        2253 :             pp_string (pp, "  ");
     195                 :             :         }
     196                 :        6841 :       pp_printf (pp, "</%s>", m_kind.c_str ());
     197                 :             :     }
     198                 :             : 
     199                 :        6884 :   if (indent)
     200                 :        2610 :     pp_newline (pp);
     201                 :        6884 : }
     202                 :             : 
     203                 :             : void
     204                 :        5428 : element::set_attr (const char *name, std::string value)
     205                 :             : {
     206                 :        5428 :   auto iter = m_attributes.find (name);
     207                 :        5428 :   if (iter == m_attributes.end ())
     208                 :        5339 :     m_key_insertion_order.push_back (name);
     209                 :        5428 :   m_attributes[name] = std::move (value);
     210                 :        5428 : }
     211                 :             : 
     212                 :             : const char *
     213                 :          12 : element::get_attr (const char *name) const
     214                 :             : {
     215                 :          12 :   auto iter = m_attributes.find (name);
     216                 :          12 :   if (iter == m_attributes.end ())
     217                 :             :     return nullptr;
     218                 :           8 :   return iter->second.c_str ();
     219                 :             : }
     220                 :             : 
     221                 :             : // struct comment : public node
     222                 :             : 
     223                 :             : void
     224                 :           8 : comment::write_as_xml (pretty_printer *pp,
     225                 :             :                        int depth, bool indent) const
     226                 :             : {
     227                 :           8 :   if (indent)
     228                 :             :     {
     229                 :           8 :       for (int i = 0; i < depth; ++i)
     230                 :           0 :         pp_string (pp, "  ");
     231                 :             :     }
     232                 :           8 :   pp_string (pp, "<!-- ");
     233                 :           8 :   write_escaped_text (pp, m_text.c_str ());
     234                 :           8 :   pp_string (pp, " -->");
     235                 :           8 :   if (indent)
     236                 :           8 :     pp_newline (pp);
     237                 :           8 : }
     238                 :             : 
     239                 :             : // struct raw : public node
     240                 :             : 
     241                 :             : void
     242                 :          18 : raw::write_as_xml (pretty_printer *pp,
     243                 :             :                    int /*depth*/, bool /*indent*/) const
     244                 :             : {
     245                 :          18 :   pp_string (pp, m_xml_src.c_str ());
     246                 :          18 : }
     247                 :             : 
     248                 :             : // class printer
     249                 :             : 
     250                 :         492 : printer::printer (element &insertion_point,
     251                 :         492 :                   bool check_popped_tags)
     252                 :         492 : : m_check_popped_tags (check_popped_tags)
     253                 :             : {
     254                 :         492 :   m_open_tags.push_back (&insertion_point);
     255                 :         492 : }
     256                 :             : 
     257                 :             : void
     258                 :        2424 : printer::push_tag (std::string name,
     259                 :             :                    bool preserve_whitespace)
     260                 :             : {
     261                 :        2424 :   push_element
     262                 :        2424 :     (std::make_unique<element> (std::move (name),
     263                 :             :                                 preserve_whitespace));
     264                 :        2424 : }
     265                 :             : 
     266                 :             : void
     267                 :        4131 : printer::push_tag_with_class (std::string name, std::string class_,
     268                 :             :                               bool preserve_whitespace)
     269                 :             : {
     270                 :        4131 :   auto new_element
     271                 :             :     = std::make_unique<element> (std::move (name),
     272                 :        4131 :                                  preserve_whitespace);
     273                 :        8262 :   new_element->set_attr ("class", class_);
     274                 :        4131 :   push_element (std::move (new_element));
     275                 :        4131 : }
     276                 :             : 
     277                 :             : /*  Pop the current topmost tag.
     278                 :             :     If m_check_popped_tags, assert that the tag we're popping is
     279                 :             :     EXPECTED_NAME.  */
     280                 :             : 
     281                 :             : void
     282                 :        6523 : printer::pop_tag (const char *expected_name ATTRIBUTE_UNUSED)
     283                 :             : {
     284                 :        6523 :   gcc_assert (!m_open_tags.empty ());
     285                 :        6523 :   if (m_check_popped_tags)
     286                 :        6242 :     gcc_assert (expected_name == get_insertion_point ()->m_kind);
     287                 :        6523 :   m_open_tags.pop_back ();
     288                 :        6523 : }
     289                 :             : 
     290                 :             : void
     291                 :         782 : printer::set_attr (const char *name, std::string value)
     292                 :             : {
     293                 :        1564 :   m_open_tags.back ()->set_attr (name, value);
     294                 :         782 : }
     295                 :             : 
     296                 :             : void
     297                 :       22856 : printer::add_text (std::string text)
     298                 :             : {
     299                 :       22856 :   element *parent = m_open_tags.back ();
     300                 :       22856 :   parent->add_text (std::move (text));
     301                 :       22856 : }
     302                 :             : 
     303                 :             : void
     304                 :        7399 : printer::add_text_from_pp (pretty_printer &pp)
     305                 :             : {
     306                 :        7399 :   element *parent = m_open_tags.back ();
     307                 :        7399 :   parent->add_text_from_pp (pp);
     308                 :        7399 : }
     309                 :             : 
     310                 :             : void
     311                 :          14 : printer::add_raw (std::string text)
     312                 :             : {
     313                 :          14 :   element *parent = m_open_tags.back ();
     314                 :          14 :   parent->add_child (std::make_unique<xml::raw> (std::move (text)));
     315                 :          14 : }
     316                 :             : 
     317                 :             : void
     318                 :        6555 : printer::push_element (std::unique_ptr<element> new_element)
     319                 :             : {
     320                 :        6555 :   gcc_assert (new_element.get ());
     321                 :        6555 :   element *parent = m_open_tags.back ();
     322                 :        6555 :   m_open_tags.push_back (new_element.get ());
     323                 :        6555 :   parent->add_child (std::move (new_element));
     324                 :        6555 : }
     325                 :             : 
     326                 :             : void
     327                 :         131 : printer::append (std::unique_ptr<node> new_node)
     328                 :             : {
     329                 :         131 :   gcc_assert (new_node.get ());
     330                 :         131 :   element *parent = m_open_tags.back ();
     331                 :         131 :   parent->add_child (std::move (new_node));
     332                 :         131 : }
     333                 :             : 
     334                 :             : element *
     335                 :        8715 : printer::get_insertion_point () const
     336                 :             : {
     337                 :        8715 :   return m_open_tags.back ();
     338                 :             : }
     339                 :             : 
     340                 :             : void
     341                 :           0 : printer::dump () const
     342                 :             : {
     343                 :           0 :   pretty_printer pp;
     344                 :           0 :   pp.set_output_stream (stderr);
     345                 :           0 :   pp_printf (&pp, "open tags: %i:", (int)m_open_tags.size ());
     346                 :           0 :   for (auto iter : m_open_tags)
     347                 :           0 :     pp_printf (&pp, " <%s>", iter->m_kind.c_str ());
     348                 :           0 :   pp_newline (&pp);
     349                 :           0 :   pp_printf (&pp, "xml:");
     350                 :           0 :   pp_newline (&pp);
     351                 :           0 :   m_open_tags[0]->write_as_xml (&pp, 1, true);
     352                 :           0 :   pp_flush (&pp);
     353                 :           0 : }
     354                 :             : 
     355                 :             : #if __GNUC__ >= 10
     356                 :             : #  pragma GCC diagnostic pop
     357                 :             : #endif
     358                 :             : 
     359                 :             : } // namespace xml
     360                 :             : 
     361                 :             : #if CHECKING_P
     362                 :             : 
     363                 :             : namespace selftest {
     364                 :             : 
     365                 :             : void
     366                 :          40 : assert_xml_print_eq (const location &loc,
     367                 :             :                      const xml::node &node,
     368                 :             :                      const char *expected_value)
     369                 :             : {
     370                 :          40 :   pretty_printer pp;
     371                 :          40 :   node.write_as_xml (&pp, 0, true);
     372                 :          40 :   ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected_value);
     373                 :          40 : }
     374                 :             : 
     375                 :             : static void
     376                 :           4 : test_no_dtd ()
     377                 :             : {
     378                 :           4 :   xml::document doc;
     379                 :           4 :   ASSERT_XML_PRINT_EQ
     380                 :             :     (doc,
     381                 :             :      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
     382                 :           4 : }
     383                 :             : 
     384                 :             : static void
     385                 :           4 : test_printer ()
     386                 :             : {
     387                 :           4 :   xml::element top ("top", false);
     388                 :           4 :   xml::printer xp (top);
     389                 :           4 :   xp.push_tag ("foo");
     390                 :           4 :   xp.add_text ("hello");
     391                 :           4 :   xp.push_tag ("bar");
     392                 :           4 :   xp.set_attr ("size", "3");
     393                 :           4 :   xp.set_attr ("color", "red");
     394                 :           4 :   xp.add_text ("world");
     395                 :           4 :   xp.push_tag ("baz");
     396                 :           4 :   xp.pop_tag ("baz");
     397                 :           4 :   xp.pop_tag ("bar");
     398                 :           4 :   xp.pop_tag ("foo");
     399                 :             : 
     400                 :           4 :   ASSERT_XML_PRINT_EQ
     401                 :             :     (top,
     402                 :             :      "<top>\n"
     403                 :             :      "  <foo>\n"
     404                 :             :      "    hello\n"
     405                 :             :      "    <bar size=\"3\" color=\"red\">\n"
     406                 :             :      "      world\n"
     407                 :             :      "      <baz/>\n"
     408                 :             :      "    </bar>\n"
     409                 :             :      "  </foo>\n"
     410                 :             :      "</top>\n");
     411                 :             : 
     412                 :           4 :   xml::element *foo = top.find_child_element ("foo");
     413                 :           4 :   ASSERT_TRUE (foo);
     414                 :           4 :   ASSERT_EQ (top.find_child_element ("not-foo"), nullptr);
     415                 :           4 :   xml::element *bar = foo->find_child_element ("bar");
     416                 :           4 :   ASSERT_TRUE (bar);
     417                 :           4 :   ASSERT_STREQ (bar->get_attr ("size"), "3");
     418                 :           4 :   ASSERT_STREQ (bar->get_attr ("color"), "red");
     419                 :           4 :   ASSERT_EQ (bar->get_attr ("airspeed-velocity"), nullptr);
     420                 :           4 : }
     421                 :             : 
     422                 :             : // Verify that element attributes preserve insertion order.
     423                 :             : 
     424                 :             : static void
     425                 :           4 : test_attribute_ordering ()
     426                 :             : {
     427                 :           4 :   xml::element top ("top", false);
     428                 :           4 :   xml::printer xp (top);
     429                 :           4 :   xp.push_tag ("chronological");
     430                 :           4 :   xp.set_attr ("maldon", "991");
     431                 :           4 :   xp.set_attr ("hastings", "1066");
     432                 :           4 :   xp.set_attr ("edgehill", "1642");
     433                 :           4 :   xp.set_attr ("naseby", "1645");
     434                 :           4 :   xp.pop_tag ("chronological");
     435                 :           4 :   xp.push_tag ("alphabetical");
     436                 :           4 :   xp.set_attr ("edgehill", "1642");
     437                 :           4 :   xp.set_attr ("hastings", "1066");
     438                 :           4 :   xp.set_attr ("maldon", "991");
     439                 :           4 :   xp.set_attr ("naseby", "1645");
     440                 :           4 :   xp.pop_tag ("alphabetical");
     441                 :             : 
     442                 :           4 :   ASSERT_XML_PRINT_EQ
     443                 :             :     (top,
     444                 :             :      "<top>\n"
     445                 :             :      "  <chronological maldon=\"991\" hastings=\"1066\" edgehill=\"1642\" naseby=\"1645\"/>\n"
     446                 :             :      "  <alphabetical edgehill=\"1642\" hastings=\"1066\" maldon=\"991\" naseby=\"1645\"/>\n"
     447                 :             :      "</top>\n");
     448                 :           4 : }
     449                 :             : 
     450                 :             : static void
     451                 :           4 : test_comment ()
     452                 :             : {
     453                 :           4 :   xml::document doc;
     454                 :           4 :   doc.add_comment ("hello");
     455                 :           4 :   doc.add_comment ("world");
     456                 :           4 :   ASSERT_XML_PRINT_EQ
     457                 :             :     (doc,
     458                 :             :      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
     459                 :             :      "<!-- hello -->\n"
     460                 :             :      "<!-- world -->\n");
     461                 :             : 
     462                 :           4 : }
     463                 :             : /* Run all of the selftests within this file.  */
     464                 :             : 
     465                 :             : void
     466                 :           4 : xml_cc_tests ()
     467                 :             : {
     468                 :           4 :   test_no_dtd ();
     469                 :           4 :   test_printer ();
     470                 :           4 :   test_attribute_ordering ();
     471                 :           4 :   test_comment ();
     472                 :           4 : }
     473                 :             : 
     474                 :             : } // namespace selftest
     475                 :             : 
     476                 :             : #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.