https://gcc.gnu.org/g:1233d79c1935f19104e76ba7aa842dc7ee6c2279
commit r16-1268-g1233d79c1935f19104e76ba7aa842dc7ee6c2279 Author: David Malcolm <dmalc...@redhat.com> Date: Fri Jun 6 13:41:27 2025 -0400 diagnostics: move xml defs to a new xml.cc While prototyping new features I'm finding it helpful to use XML beyond the "experimental-html" diagnostics sink. Move the implementation of the xml classes to their own file. No functional change intended. gcc/ChangeLog: * Makefile.in (OBJS-libcommon): Add xml.o. * diagnostic-format-html.cc (namespace xml): Move implementation to xml.cc (selftest::test_printer): Likewise. (selftest::test_attribute_ordering): Likewise. (selftest::diagnostic_format_html_cc_tests): Don't call the moved tests here; they will be called from xml_cc_tests in xml.cc. * selftest-run-tests.cc (selftest::run_tests): Call xml_cc_tests. * selftest.h (selftest::xml_cc_tests): New decl. * xml.cc: New file, based on material from diagnostic-format-html.cc. Signed-off-by: David Malcolm <dmalc...@redhat.com> Diff: --- gcc/Makefile.in | 1 + gcc/diagnostic-format-html.cc | 313 ------------------------------------ gcc/selftest-run-tests.cc | 1 + gcc/selftest.h | 1 + gcc/xml.cc | 358 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 361 insertions(+), 313 deletions(-) diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 49869531bc8a..fe20b655477f 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1862,6 +1862,7 @@ OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \ edit-context.o \ pretty-print.o intl.o \ json.o json-parsing.o \ + xml.o \ sbitmap.o \ vec.o input.o hash-table.o ggc-none.o memory-block.o \ selftest.o selftest-diagnostic.o sort.o \ diff --git a/gcc/diagnostic-format-html.cc b/gcc/diagnostic-format-html.cc index 05d4273c2c61..076790bfff7a 100644 --- a/gcc/diagnostic-format-html.cc +++ b/gcc/diagnostic-format-html.cc @@ -49,256 +49,6 @@ html_generation_options::html_generation_options () { } -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"); - pp_string (pp, "<!DOCTYPE html\n" - " PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n" - " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"); - if (indent) - pp_newline (pp); - 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 - class html_builder; /* Concrete buffering implementation subclass for HTML output. */ @@ -1288,67 +1038,6 @@ test_metadata () } } -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 @@ -1357,8 +1046,6 @@ diagnostic_format_html_cc_tests () auto_fix_quotes fix_quotes; test_simple_log (); test_metadata (); - test_printer (); - test_attribute_ordering (); } } // namespace selftest diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc index 0090e56bab6a..df49a67c9c91 100644 --- a/gcc/selftest-run-tests.cc +++ b/gcc/selftest-run-tests.cc @@ -80,6 +80,7 @@ selftest::run_tests () optinfo_emit_json_cc_tests (); ordered_hash_map_tests_cc_tests (); splay_tree_cc_tests (); + xml_cc_tests (); /* Mid-level data structures. */ input_cc_tests (); diff --git a/gcc/selftest.h b/gcc/selftest.h index c5ea6de30137..94acf62b434e 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -276,6 +276,7 @@ extern void typed_splay_tree_cc_tests (); extern void vec_cc_tests (); extern void vec_perm_indices_cc_tests (); extern void wide_int_cc_tests (); +extern void xml_cc_tests (); extern int num_passes; diff --git a/gcc/xml.cc b/gcc/xml.cc new file mode 100644 index 000000000000..e75884066f30 --- /dev/null +++ b/gcc/xml.cc @@ -0,0 +1,358 @@ +/* XML support for diagnostics. + Copyright (C) 2024-2025 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalc...@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"); + pp_string (pp, "<!DOCTYPE html\n" + " PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n" + " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"); + if (indent) + pp_newline (pp); + 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_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_printer (); + test_attribute_ordering (); +} + +} // namespace selftest + +#endif /* CHECKING_P */