aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2025-06-06 13:41:27 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2025-06-06 13:41:27 -0400
commit1233d79c1935f19104e76ba7aa842dc7ee6c2279 (patch)
tree9b09796d734557b6811d396f5e6d8e65129aed73
parent0401957b86fa29b25acf3e960e430e6a7c9196eb (diff)
downloadgcc-1233d79c1935f19104e76ba7aa842dc7ee6c2279.zip
gcc-1233d79c1935f19104e76ba7aa842dc7ee6c2279.tar.gz
gcc-1233d79c1935f19104e76ba7aa842dc7ee6c2279.tar.bz2
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 <dmalcolm@redhat.com>
-rw-r--r--gcc/Makefile.in1
-rw-r--r--gcc/diagnostic-format-html.cc313
-rw-r--r--gcc/selftest-run-tests.cc1
-rw-r--r--gcc/selftest.h1
-rw-r--r--gcc/xml.cc358
5 files changed, 361 insertions, 313 deletions
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 49869531..fe20b65 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 05d4273..076790b 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, "&apos;");
- break;
- case '"':
- pp_string (pp, "&quot;");
- break;
- case '&':
- pp_string (pp, "&amp;");
- break;
- case '<':
- pp_string (pp, "&lt;");
- break;
- case '>':
- pp_string (pp, "&gt;");
- 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 0090e56..df49a67 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 c5ea6de..94acf62 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 0000000..e758840
--- /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 <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, "&apos;");
+ break;
+ case '"':
+ pp_string (pp, "&quot;");
+ break;
+ case '&':
+ pp_string (pp, "&amp;");
+ break;
+ case '<':
+ pp_string (pp, "&lt;");
+ break;
+ case '>':
+ pp_string (pp, "&gt;");
+ 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 */