LCOV - code coverage report
Current view: top level - gcc - xml.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 93.2 % 237 221
Test Date: 2026-02-28 14:20:25 Functions: 96.9 % 32 31
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* XML support for diagnostics.
       2              :    Copyright (C) 2024-2026 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        12666 : write_escaped_text (pretty_printer *pp, const char *text)
      47              : {
      48        12666 :   gcc_assert (text);
      49              : 
      50       176771 :   for (const char *p = text; *p; ++p)
      51              :     {
      52       164105 :       char ch = *p;
      53       164105 :       switch (ch)
      54              :         {
      55       162118 :         default:
      56       162118 :           pp_character (pp, ch);
      57       162118 :           break;
      58          801 :         case '\'':
      59          801 :           pp_string (pp, "&apos;");
      60          801 :           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          179 :         case '<':
      68          179 :           pp_string (pp, "&lt;");
      69          179 :           break;
      70          177 :         case '>':
      71          177 :           pp_string (pp, "&gt;");
      72          177 :           break;
      73              :         }
      74              :     }
      75        12666 : }
      76              : 
      77              : /* struct node.  */
      78              : 
      79              : void
      80           14 : node::dump (FILE *out) const
      81              : {
      82           14 :   pretty_printer pp;
      83           14 :   pp.set_output_stream (out);
      84           14 :   write_as_xml (&pp, 0, true);
      85           14 :   pp_flush (&pp);
      86           14 : }
      87              : 
      88              : /* struct text : public node.  */
      89              : 
      90              : void
      91         5810 : text::write_as_xml (pretty_printer *pp, int depth, bool indent) const
      92              : {
      93         5810 :   if (indent)
      94              :     {
      95           28 :       for (int i = 0; i < depth; ++i)
      96           20 :         pp_string (pp, "  ");
      97              :     }
      98         5810 :   write_escaped_text (pp, m_str.c_str ());
      99         5810 :   if (indent)
     100            8 :     pp_newline (pp);
     101         5810 : }
     102              : 
     103              : /* struct node_with_children : public node.  */
     104              : 
     105              : void
     106        14667 : node_with_children::add_child (std::unique_ptr<node> node)
     107              : {
     108        14667 :   gcc_assert (node.get ());
     109        14667 :   m_children.push_back (std::move (node));
     110        14667 : }
     111              : 
     112              : void
     113        31453 : node_with_children::add_text (std::string str)
     114              : {
     115              :   // Consolidate runs of text
     116        31453 :   if (!m_children.empty ())
     117        26129 :     if (text *t = m_children.back ()->dyn_cast_text ())
     118              :       {
     119        25617 :         t->m_str += std::move (str);
     120        25617 :         return;
     121              :       }
     122         5836 :   add_child (std::make_unique <text> (std::move (str)));
     123              : }
     124              : 
     125              : void
     126         7480 : node_with_children::add_text_from_pp (pretty_printer &pp)
     127              : {
     128         7480 :   add_text (pp_formatted_text (&pp));
     129         7480 : }
     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           26 : document::write_as_xml (pretty_printer *pp, int depth, bool indent) const
     151              : {
     152           26 :   pp_string (pp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
     153           26 :   if (m_doctypedecl)
     154           18 :     m_doctypedecl->write_as_xml (pp, depth, indent);
     155           52 :   for (auto &iter : m_children)
     156           26 :     iter->write_as_xml (pp, depth, indent);
     157           26 : }
     158              : 
     159              : /* struct element : public node_with_children.  */
     160              : 
     161              : void
     162         8957 : element::write_as_xml (pretty_printer *pp, int depth, bool indent) const
     163              : {
     164         8957 :   if (indent)
     165              :     {
     166        14460 :       for (int i = 0; i < depth; ++i)
     167        10663 :         pp_string (pp, "  ");
     168              :     }
     169              : 
     170         8957 :   pp_printf (pp, "<%s", m_kind.c_str ());
     171        15805 :   for (auto &key : m_key_insertion_order)
     172              :     {
     173         6848 :       auto iter = m_attributes.find (key);
     174         6848 :       if (iter != m_attributes.end ())
     175              :         {
     176         6848 :           pp_printf (pp, " %s=\"", key.c_str ());
     177         6848 :           write_escaped_text (pp, iter->second.c_str ());
     178         6848 :           pp_string (pp, "\"");
     179              :         }
     180              :     }
     181         8957 :   if (m_children.empty ())
     182           52 :     pp_string (pp, "/>");
     183              :   else
     184              :     {
     185         8905 :       const bool indent_children = m_preserve_whitespace ? false : indent;
     186         8905 :       pp_string (pp, ">");
     187         8905 :       if (indent_children)
     188         1112 :         pp_newline (pp);
     189        23308 :       for (auto &child : m_children)
     190        14403 :         child->write_as_xml (pp, depth + 1, indent_children);
     191         8905 :       if (indent_children)
     192              :         {
     193         3564 :           for (int i = 0; i < depth; ++i)
     194         2452 :             pp_string (pp, "  ");
     195              :         }
     196         8905 :       pp_printf (pp, "</%s>", m_kind.c_str ());
     197              :     }
     198              : 
     199         8957 :   if (indent)
     200         3797 :     pp_newline (pp);
     201         8957 : }
     202              : 
     203              : void
     204         6950 : element::set_attr (const char *name, std::string value)
     205              : {
     206         6950 :   auto iter = m_attributes.find (name);
     207         6950 :   if (iter == m_attributes.end ())
     208         6858 :     m_key_insertion_order.push_back (name);
     209         6950 :   m_attributes[name] = std::move (value);
     210         6950 : }
     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           67 : raw::write_as_xml (pretty_printer *pp,
     243              :                    int /*depth*/, bool /*indent*/) const
     244              : {
     245           67 :   pp_string (pp, m_xml_src.c_str ());
     246           67 : }
     247              : 
     248              : // class printer
     249              : 
     250          741 : printer::printer (element &insertion_point,
     251          741 :                   bool check_popped_tags)
     252          741 : : m_check_popped_tags (check_popped_tags)
     253              : {
     254          741 :   m_open_tags.push_back (&insertion_point);
     255          741 : }
     256              : 
     257              : void
     258         4246 : printer::push_tag (std::string name,
     259              :                    bool preserve_whitespace)
     260              : {
     261         4246 :   push_element
     262         4246 :     (std::make_unique<element> (std::move (name),
     263              :                                 preserve_whitespace));
     264         4246 : }
     265              : 
     266              : void
     267         4141 : printer::push_tag_with_class (std::string name, std::string class_,
     268              :                               bool preserve_whitespace)
     269              : {
     270         4141 :   auto new_element
     271              :     = std::make_unique<element> (std::move (name),
     272         4141 :                                  preserve_whitespace);
     273         8282 :   new_element->set_attr ("class", class_);
     274         4141 :   push_element (std::move (new_element));
     275         4141 : }
     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         8347 : printer::pop_tag (const char *expected_name ATTRIBUTE_UNUSED)
     283              : {
     284         8347 :   gcc_assert (!m_open_tags.empty ());
     285         8347 :   if (m_check_popped_tags)
     286         8066 :     gcc_assert (expected_name == get_insertion_point ()->m_kind);
     287         8347 :   m_open_tags.pop_back ();
     288         8347 : }
     289              : 
     290              : void
     291         2223 : printer::set_attr (const char *name, std::string value)
     292              : {
     293         4446 :   m_open_tags.back ()->set_attr (name, value);
     294         2223 : }
     295              : 
     296              : void
     297        23727 : printer::add_text (std::string text)
     298              : {
     299        23727 :   element *parent = m_open_tags.back ();
     300        23727 :   parent->add_text (std::move (text));
     301        23727 : }
     302              : 
     303              : void
     304         7480 : printer::add_text_from_pp (pretty_printer &pp)
     305              : {
     306         7480 :   element *parent = m_open_tags.back ();
     307         7480 :   parent->add_text_from_pp (pp);
     308         7480 : }
     309              : 
     310              : void
     311           18 : printer::add_raw (std::string text)
     312              : {
     313           18 :   element *parent = m_open_tags.back ();
     314           18 :   parent->add_child (std::make_unique<xml::raw> (std::move (text)));
     315           18 : }
     316              : 
     317              : void
     318         8387 : printer::push_element (std::unique_ptr<element> new_element)
     319              : {
     320         8387 :   gcc_assert (new_element.get ());
     321         8387 :   element *parent = m_open_tags.back ();
     322         8387 :   m_open_tags.push_back (new_element.get ());
     323         8387 :   parent->add_child (std::move (new_element));
     324         8387 : }
     325              : 
     326              : void
     327          182 : printer::append (std::unique_ptr<node> new_node)
     328              : {
     329          182 :   gcc_assert (new_node.get ());
     330          182 :   element *parent = m_open_tags.back ();
     331          182 :   parent->add_child (std::move (new_node));
     332          182 : }
     333              : 
     334              : element *
     335        10558 : printer::get_insertion_point () const
     336              : {
     337        10558 :   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.4-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.