diff options
Diffstat (limited to 'gcc/xml.cc')
-rw-r--r-- | gcc/xml.cc | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/gcc/xml.cc b/gcc/xml.cc new file mode 100644 index 0000000..6c95288 --- /dev/null +++ b/gcc/xml.cc @@ -0,0 +1,367 @@ +/* XML support for diagnostics. + Copyright (C) 2024-2025 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#define INCLUDE_MAP +#define INCLUDE_STRING +#define INCLUDE_VECTOR +#include "system.h" +#include "coretypes.h" +#include "xml.h" +#include "xml-printer.h" +#include "pretty-print.h" +#include "selftest.h" + +namespace xml { + +/* Disable warnings about quoting issues in the pp_xxx calls below + that (intentionally) don't follow GCC diagnostic conventions. */ +#if __GNUC__ >= 10 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat-diag" +#endif + + +/* Implementation. */ + +static void +write_escaped_text (pretty_printer *pp, const char *text) +{ + gcc_assert (text); + + for (const char *p = text; *p; ++p) + { + char ch = *p; + switch (ch) + { + default: + pp_character (pp, ch); + break; + case '\'': + pp_string (pp, "'"); + break; + case '"': + pp_string (pp, """); + break; + case '&': + pp_string (pp, "&"); + break; + case '<': + pp_string (pp, "<"); + break; + case '>': + pp_string (pp, ">"); + break; + } + } +} + +/* struct node. */ + +void +node::dump (FILE *out) const +{ + pretty_printer pp; + pp.set_output_stream (out); + write_as_xml (&pp, 0, true); + pp_flush (&pp); +} + +/* struct text : public node. */ + +void +text::write_as_xml (pretty_printer *pp, int depth, bool indent) const +{ + if (indent) + { + for (int i = 0; i < depth; ++i) + pp_string (pp, " "); + } + write_escaped_text (pp, m_str.c_str ()); + if (indent) + pp_newline (pp); +} + +/* struct node_with_children : public node. */ + +void +node_with_children::add_child (std::unique_ptr<node> node) +{ + gcc_assert (node.get ()); + m_children.push_back (std::move (node)); +} + +void +node_with_children::add_text (std::string str) +{ + // Consolidate runs of text + if (!m_children.empty ()) + if (text *t = m_children.back ()->dyn_cast_text ()) + { + t->m_str += std::move (str); + return; + } + add_child (std::make_unique <text> (std::move (str))); +} + + +/* struct document : public node_with_children. */ + +void +document::write_as_xml (pretty_printer *pp, int depth, bool indent) const +{ + pp_string (pp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + if (m_doctypedecl) + m_doctypedecl->write_as_xml (pp, depth, indent); + for (auto &iter : m_children) + iter->write_as_xml (pp, depth, indent); +} + +/* struct element : public node_with_children. */ + +void +element::write_as_xml (pretty_printer *pp, int depth, bool indent) const +{ + if (indent) + { + for (int i = 0; i < depth; ++i) + pp_string (pp, " "); + } + + pp_printf (pp, "<%s", m_kind.c_str ()); + for (auto &key : m_key_insertion_order) + { + auto iter = m_attributes.find (key); + if (iter != m_attributes.end ()) + { + pp_printf (pp, " %s=\"", key.c_str ()); + write_escaped_text (pp, iter->second.c_str ()); + pp_string (pp, "\""); + } + } + if (m_children.empty ()) + pp_string (pp, "/>"); + else + { + const bool indent_children = m_preserve_whitespace ? false : indent; + pp_string (pp, ">"); + if (indent_children) + pp_newline (pp); + for (auto &child : m_children) + child->write_as_xml (pp, depth + 1, indent_children); + if (indent_children) + { + for (int i = 0; i < depth; ++i) + pp_string (pp, " "); + } + pp_printf (pp, "</%s>", m_kind.c_str ()); + } + + if (indent) + pp_newline (pp); +} + +void +element::set_attr (const char *name, std::string value) +{ + auto iter = m_attributes.find (name); + if (iter == m_attributes.end ()) + m_key_insertion_order.push_back (name); + m_attributes[name] = std::move (value); +} + +// struct raw : public node + +void +raw::write_as_xml (pretty_printer *pp, + int /*depth*/, bool /*indent*/) const +{ + pp_string (pp, m_xml_src.c_str ()); +} + +#if __GNUC__ >= 10 +# pragma GCC diagnostic pop +#endif + +// class printer + +printer::printer (element &insertion_point) +{ + m_open_tags.push_back (&insertion_point); +} + +void +printer::push_tag (std::string name, + bool preserve_whitespace) +{ + push_element + (std::make_unique<element> (std::move (name), + preserve_whitespace)); +} + +void +printer::push_tag_with_class (std::string name, std::string class_, + bool preserve_whitespace) +{ + auto new_element + = std::make_unique<element> (std::move (name), + preserve_whitespace); + new_element->set_attr ("class", class_); + push_element (std::move (new_element)); +} + +void +printer::pop_tag () +{ + m_open_tags.pop_back (); +} + +void +printer::set_attr (const char *name, std::string value) +{ + m_open_tags.back ()->set_attr (name, value); +} + +void +printer::add_text (std::string text) +{ + element *parent = m_open_tags.back (); + parent->add_text (std::move (text)); +} + +void +printer::add_raw (std::string text) +{ + element *parent = m_open_tags.back (); + parent->add_child (std::make_unique<xml::raw> (std::move (text))); +} + +void +printer::push_element (std::unique_ptr<element> new_element) +{ + element *parent = m_open_tags.back (); + m_open_tags.push_back (new_element.get ()); + parent->add_child (std::move (new_element)); +} + +void +printer::append (std::unique_ptr<node> new_node) +{ + element *parent = m_open_tags.back (); + parent->add_child (std::move (new_node)); +} + +element * +printer::get_insertion_point () const +{ + return m_open_tags.back (); +} + +} // namespace xml + +#if CHECKING_P + +namespace selftest { + +static void +test_no_dtd () +{ + xml::document doc; + pretty_printer pp; + doc.write_as_xml (&pp, 0, true); + ASSERT_STREQ + (pp_formatted_text (&pp), + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); +} + +static void +test_printer () +{ + xml::element top ("top", false); + xml::printer xp (top); + xp.push_tag ("foo"); + xp.add_text ("hello"); + xp.push_tag ("bar"); + xp.set_attr ("size", "3"); + xp.set_attr ("color", "red"); + xp.add_text ("world"); + xp.push_tag ("baz"); + xp.pop_tag (); + xp.pop_tag (); + xp.pop_tag (); + + pretty_printer pp; + top.write_as_xml (&pp, 0, true); + ASSERT_STREQ + (pp_formatted_text (&pp), + "<top>\n" + " <foo>\n" + " hello\n" + " <bar size=\"3\" color=\"red\">\n" + " world\n" + " <baz/>\n" + " </bar>\n" + " </foo>\n" + "</top>\n"); +} + +// Verify that element attributes preserve insertion order. + +static void +test_attribute_ordering () +{ + xml::element top ("top", false); + xml::printer xp (top); + xp.push_tag ("chronological"); + xp.set_attr ("maldon", "991"); + xp.set_attr ("hastings", "1066"); + xp.set_attr ("edgehill", "1642"); + xp.set_attr ("naseby", "1645"); + xp.pop_tag (); + xp.push_tag ("alphabetical"); + xp.set_attr ("edgehill", "1642"); + xp.set_attr ("hastings", "1066"); + xp.set_attr ("maldon", "991"); + xp.set_attr ("naseby", "1645"); + xp.pop_tag (); + + pretty_printer pp; + top.write_as_xml (&pp, 0, true); + ASSERT_STREQ + (pp_formatted_text (&pp), + "<top>\n" + " <chronological maldon=\"991\" hastings=\"1066\" edgehill=\"1642\" naseby=\"1645\"/>\n" + " <alphabetical edgehill=\"1642\" hastings=\"1066\" maldon=\"991\" naseby=\"1645\"/>\n" + "</top>\n"); +} + +/* Run all of the selftests within this file. */ + +void +xml_cc_tests () +{ + test_no_dtd (); + test_printer (); + test_attribute_ordering (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ |