mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
diagnostics: rework experimental-html output [PR116792]
This patch reworks the HTML output from the the option -fdiagnostics-add-output=experimental-html so that for source quoting and path printing, rather than simply adding the textual output inside <pre> elements, it breaks up the output into HTML tags reflecting the structure of the output, using CSS, SVG and a little javascript to help the user navigate the diagnostics and the events within any paths. This uses ideas from the patch I posted in: https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558603.html but reworks the above patch so that: * rather than printing source to a pretty_printer, the HTML is created by building a DOM tree in memory, using a new xml::printer class. This should be less error-prone than the pretty_printer approach, since it ought to solve escaping issues. Instead of a text vs html boolean, the code is generalized via templates with to_text vs to_html sinks. This templatization applies both to path-printing and diagnostic-show-locus.cc. * the HTML output can have multiple diagnostics and multiple paths rather than just a single path. The javascript keyboard controls now cycle through all diagnostics and all events within them An example of the output can be seen at: https://dmalcolm.fedorapeople.org/gcc/2025-05-27/malloc-1.c.html where the keys "j" and "k" cycle through diagnostics and events within them. gcc/ChangeLog: PR other/116792 * diagnostic-format-html.cc: Define INCLUDE_STRING. Include "xml.h", "xml-printer.h", and "json.h". (html_generation_options::html_generation_options): New. (namespace xml): Move decls to xml.h and convert from using label_text to std::string. (xml::text::write_as_xml): Reimplement indentation so it is done by this node, rather than the parent. (xml::node_with_children::add_text): Convert from label_text to std::string. Consolidate runs of text into a single node. (xml::document::write_as_xml): Reimplement indentation. (xml::element::write_as_xml): Reimplement indentation so it is done by this node, rather than the parent. Convert from label_text to std::string. Update attribute-printing to new representation to preserve insertion order. (xml::element::set_attr): Convert from label_text to std::string. Record insertion order. (xml::raw::write_as_xml): New. (xml::printer::printer): New. (xml::printer::push_tag): New. (xml::printer::push_tag_with_class): New. (xml::printer::pop_tag): New. (xml::printer::set_attr): New. (xml::printer::add_text): New. (xml::printer::add_raw): New. (xml::printer::push_element): New. (xml::printer::append): New. (xml::printer::get_insertion_point): New. (html_builder::add_focus_id): New. (html_builder::m_html_gen_opts): New field. (html_builder::m_head_element): New field. (html_builder::m_next_diag_id): New field. (html_builder::m_ui_focus_ids): New field. (make_div): Convert from label_text to std::string. (make_span): Likewise. (HTML_STYLE): New. (HTML_SCRIPT): New. (html_builder::html_builder): Fix indentation. Add "html_gen_opts" param. Initialize new fields. Reimplement using xml::printer. Optionally add style and script tags. (class html_path_label_writer): New. (html_builder::make_element_for_diagnostic): Convert from label_text to std::string. Set "id" on "gcc-diagnostic" and "gcc-message" <div> elements; add the latter to the focus ids. Use diagnostic_context::maybe_show_locus_as_html rather than html_builder::make_element_for_source. Use print_path_as_html rather than html_builder::make_element_for_path. (html_builder::make_element_for_source): Drop. (html_builder::make_element_for_path): Drop. (html_builder::make_element_for_patch): Convert from label_text to std::string. (html_builder::make_metadata_element): Likewise. Use xml::printer. (html_builder::make_element_for_metadata): Convert from label_text to std::string. (html_builder::emit_diagram): Expand comment. (html_builder::flush_to_file): Write out initializer for "focus_ids" into javascript. (html_output_format::html_output_format): Add param "html_gen_opts" and use it to initialize m_builder. (html_file_output_format::html_file_output_format): Likewise, to initialize base class. (make_html_sink): Likewise, to pass to ctor. (selftest::test_html_diagnostic_context::test_html_diagnostic_context): Set up html_generation_options. (selftest::html_buffered_output_format::html_buffered_output_format): Add html_gen_opts param. (selftest::test_simple_log): Add id attributes to expected text for "gcc-diagnostic" and "gcc-message" elements. Update whitespace for indentation fixes. (selftest::test_metadata): Update whitespace for indentation fixes. (selftest::test_printer): New selftest. (selftest::test_attribute_ordering): New selftest. (selftest::diagnostic_format_html_cc_tests): Call the new selftests. * diagnostic-format-html.h (struct html_generation_options): New. (make_html_sink): Add "html_gen_opts" param. (print_path_as_html): New decl. * diagnostic-path-output.cc: Define INCLUDE_MAP. Add includes of "diagnostic-format-html.h", "xml.h", and "xml-printer.h". (path_print_policy::path_print_policy): Add ctor. (path_print_policy::get_diagram_theme): Fix whitespace. (struct stack_frame): New. (begin_html_stack_frame): New function. (end_html_stack_frame): New function. (emit_svg_arrow): New function. (event_range::print): Rename to... (event_range::print_as_text): ...this. Update call to diagnostic_start_span. (event_range::print_as_html): New, based on the above, but ported from pretty_printer to xml::printer. (thread_event_printer::print_swimlane_for_event_range): Rename to... (thread_event_printer::print_swimlane_for_event_range_as_text): ...this. Update for renaming of event_range::print to event_range::print_as_text. (thread_event_printer::print_swimlane_for_event_range_as_html): New. (print_path_summary_as_text): Update for "_as_text" renaming. (print_path_summary_as_html): New. (print_path_as_html): New. * diagnostic-show-locus.cc: Add defines of INCLUDE_MAP and INCLUDE_STRING. Add includes of "xml.h" and "xml-printer.h". (struct char_display_policy): Replace "m_print_cb" with "m_print_text_cb" and "m_print_html_cb". (struct to_text): New. (struct to_html): New. (get_printer): New. (default_diagnostic_start_span_fn<to_text>): New. (default_diagnostic_start_span_fn<to_html>): New. (class layout): Update "friend class layout_printer;" for template. (enum class margin_kind): New. (class layout_printer): Convert into a template. (layout_printer::m_pp): Replace field with... (layout_printer::m_sink): ...this. (layout_printer::m_colorizer): Drop field in favor of a pointer in the "to_text" sink. (default_print_decoded_ch): Convert into a template. (escape_as_bytes_print): Likewise. (escape_as_unicode_print): Likewise. (make_char_policy): Update to use both text and html callbacks. (layout_printer::print_gap_in_line_numbering): Replace with... (layout_printer<to_text>::print_gap_in_line_numbering): ...this (layout_printer<to_html>::print_gap_in_line_numbering): ...and this. (layout_printer::print_source_line): Convert to template, using m_sink. (layout_printer::print_leftmost_column): Likewise. (layout_printer::start_annotation_line): Likewise. (layout_printer<to_text>::end_line): New. (layout_printer<to_html>::end_line): New. (layout_printer::print_annotation_line): Convert to template, using m_sink. (class line_label): Add field m_original_range_idx. (layout_printer<to_text>::begin_label): New. (layout_printer<to_html>::begin_label): New. (layout_printer<to_text>::end_label): New. (layout_printer<to_html>::end_label): New. (layout_printer::print_any_labels): Convert to template, using m_sink. (layout_printer::print_leading_fixits): Likewise. (layout_printer::print_trailing_fixits): Likewise. (layout_printer::print_newline): Drop. (layout_printer::move_to_column): Convert to template, using m_sink. (layout_printer::show_ruler): Likewise. (layout_printer::print_line): Likewise. (layout_printer::print_any_right_to_left_edge_lines): Likewise. (layout_printer::layout_printer): Likewise. (diagnostic_context::maybe_show_locus_as_html): New. (diagnostic_source_print_policy::diagnostic_source_print_policy): Update for split of start_span_cb into text vs html variants. (diagnostic_source_print_policy::print): Update for use of templates; use to_text. (diagnostic_source_print_policy::print_as_html): New. (layout_printer::print): Convert to template, using m_sink. (selftest::make_element_for_locus): New. (selftest::make_raw_html_for_locus): New. (selftest::test_layout_x_offset_display_utf8): Update for use of templates. (selftest::test_layout_x_offset_display_tab): Likewise. (selftest::test_one_liner_caret_and_range): Add test coverage of HTML output. (selftest::test_one_liner_labels): Likewise. * diagnostic.cc (diagnostic_context::initialize): Update for split of start_span_cb into text vs html variants. (default_diagnostic_start_span_fn): Move to diagnostic-show-locus.cc, converting to template. * diagnostic.h (class xml::printer): New forward decl. (diagnostic_start_span_fn): Replace typedef with "using", converting to a template. (struct to_text): New forward decl. (struct to_html): New forward decl. (get_printer): New decl. (diagnostic_location_print_policy::print_text_span_start): New decl. (diagnostic_location_print_policy::print_html_span_start): New decl. (class html_label_writer): New. (diagnostic_source_print_policy::print_as_html): New decl. (diagnostic_source_print_policy::get_start_span_fn): Replace with... (diagnostic_source_print_policy::get_text_start_span_fn): ...this (diagnostic_source_print_policy::get_html_start_span_fn): ...and this (diagnostic_source_print_policy::m_start_span_cb): Replace with... (diagnostic_source_print_policy::m_text_start_span_cb): ...this (diagnostic_source_print_policy::m_html_start_span_cb): ...and this. (diagnostic_context::maybe_show_locus_as_html): New decl. (diagnostic_context::m_text_callbacks::m_start_span): Replace with... (diagnostic_context::m_text_callbacks::m_text_start_span): ...this (diagnostic_context::m_text_callbacks::m_html_start_span): ...and this. (diagnostic_start_span): Update for template change. (diagnostic_show_locus_as_html): New inline function. (default_diagnostic_start_span_fn): Convert to template. * doc/invoke.texi (experimental-html): Add "css" and "javascript" keys. * opts-diagnostic.cc (html_scheme_handler::make_sink): Likewise. * selftest-diagnostic.cc (selftest::test_diagnostic_context::start_span_cb): Update for template changes. * selftest-diagnostic.h (selftest::test_diagnostic_context::start_span_cb): Likewise. * xml-printer.h: New file. * xml.h: New file, based on material in diagnostic-format-html.cc, but using std::string rather than label_text. (xml::element::m_key_insertion_order): New field. (struct xml::raw): New. gcc/fortran/ChangeLog PR other/116792 * error.cc (gfc_diagnostic_start_span): Update for diagnostic.h changes. gcc/testsuite/ChangeLog: PR other/116792 * gcc.dg/html-output/missing-semicolon.c: Add ":javascript=no" to html output. * gcc.dg/html-output/missing-semicolon.py: Move repeated definitions into lib/htmltest.py. * gcc.dg/plugin/diagnostic_group_plugin.cc: Update for template changes. * gcc.dg/plugin/diagnostic-test-metadata-html.c: Add ":javascript=no" to html output. Add "-fdiagnostics-show-line-numbers". * gcc.dg/plugin/diagnostic-test-metadata-html.py: Move repeated definitions into lib/htmltest.py. Add checks of annotated source. * gcc.dg/plugin/diagnostic-test-paths-2.c: Add ":javascript=no" to html output. * gcc.dg/plugin/diagnostic-test-paths-2.py: Move repeated definitions into lib/htmltest.py. Add checks of execution path. * gcc.dg/plugin/diagnostic-test-paths-4.c: Add -fdiagnostics-add-output=experimental-html:javascript=no. Add invocation ot diagnostic-test-paths-4.py. * gcc.dg/plugin/diagnostic-test-paths-4.py: New test script. * gcc.dg/plugin/diagnostic-test-show-locus-bw-line-numbers.c: Add -fdiagnostics-add-output=experimental-html:javascript=no. Add invocation of diagnostic-test-show-locus.py. * gcc.dg/plugin/diagnostic-test-show-locus.py: New test script. * lib/htmltest.py: New test support script. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
|
||||
#include "config.h"
|
||||
#define INCLUDE_MAP
|
||||
#define INCLUDE_STRING
|
||||
#define INCLUDE_VECTOR
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
@@ -36,6 +37,17 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "pretty-print-urlifier.h"
|
||||
#include "edit-context.h"
|
||||
#include "intl.h"
|
||||
#include "xml.h"
|
||||
#include "xml-printer.h"
|
||||
#include "json.h"
|
||||
|
||||
// struct html_generation_options
|
||||
|
||||
html_generation_options::html_generation_options ()
|
||||
: m_css (true),
|
||||
m_javascript (true)
|
||||
{
|
||||
}
|
||||
|
||||
namespace xml {
|
||||
|
||||
@@ -46,57 +58,6 @@ namespace xml {
|
||||
# pragma GCC diagnostic ignored "-Wformat-diag"
|
||||
#endif
|
||||
|
||||
struct node
|
||||
{
|
||||
virtual ~node () {}
|
||||
virtual void write_as_xml (pretty_printer *pp,
|
||||
int depth, bool indent) const = 0;
|
||||
void dump (FILE *out) const;
|
||||
void DEBUG_FUNCTION dump () const { dump (stderr); }
|
||||
};
|
||||
|
||||
struct text : public node
|
||||
{
|
||||
text (label_text str)
|
||||
: m_str (std::move (str))
|
||||
{}
|
||||
|
||||
void write_as_xml (pretty_printer *pp,
|
||||
int depth, bool indent) const final override;
|
||||
|
||||
label_text m_str;
|
||||
};
|
||||
|
||||
struct node_with_children : public node
|
||||
{
|
||||
void add_child (std::unique_ptr<node> node);
|
||||
void add_text (label_text str);
|
||||
|
||||
std::vector<std::unique_ptr<node>> m_children;
|
||||
};
|
||||
|
||||
struct document : public node_with_children
|
||||
{
|
||||
void write_as_xml (pretty_printer *pp,
|
||||
int depth, bool indent) const final override;
|
||||
};
|
||||
|
||||
struct element : public node_with_children
|
||||
{
|
||||
element (const char *kind, bool preserve_whitespace)
|
||||
: m_kind (kind),
|
||||
m_preserve_whitespace (preserve_whitespace)
|
||||
{}
|
||||
|
||||
void write_as_xml (pretty_printer *pp,
|
||||
int depth, bool indent) const final override;
|
||||
|
||||
void set_attr (const char *name, label_text value);
|
||||
|
||||
const char *m_kind;
|
||||
bool m_preserve_whitespace;
|
||||
std::map<const char *, label_text> m_attributes;
|
||||
};
|
||||
|
||||
/* Implementation. */
|
||||
|
||||
@@ -146,9 +107,16 @@ node::dump (FILE *out) const
|
||||
/* struct text : public node. */
|
||||
|
||||
void
|
||||
text::write_as_xml (pretty_printer *pp, int /*depth*/, bool /*indent*/) const
|
||||
text::write_as_xml (pretty_printer *pp, int depth, bool indent) const
|
||||
{
|
||||
write_escaped_text (pp, m_str.get ());
|
||||
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. */
|
||||
@@ -161,9 +129,15 @@ node_with_children::add_child (std::unique_ptr<node> node)
|
||||
}
|
||||
|
||||
void
|
||||
node_with_children::add_text (label_text str)
|
||||
node_with_children::add_text (std::string str)
|
||||
{
|
||||
gcc_assert (str.get ());
|
||||
// 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)));
|
||||
}
|
||||
|
||||
@@ -177,6 +151,8 @@ document::write_as_xml (pretty_printer *pp, int depth, bool indent) const
|
||||
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);
|
||||
}
|
||||
@@ -188,48 +164,139 @@ element::write_as_xml (pretty_printer *pp, int depth, bool indent) const
|
||||
{
|
||||
if (indent)
|
||||
{
|
||||
pp_newline (pp);
|
||||
for (int i = 0; i < depth; ++i)
|
||||
pp_string (pp, " ");
|
||||
}
|
||||
|
||||
if (m_preserve_whitespace)
|
||||
indent = false;
|
||||
|
||||
pp_printf (pp, "<%s", m_kind);
|
||||
for (auto &attr : m_attributes)
|
||||
pp_printf (pp, "<%s", m_kind.c_str ());
|
||||
for (auto &key : m_key_insertion_order)
|
||||
{
|
||||
pp_printf (pp, " %s=\"", attr.first);
|
||||
write_escaped_text (pp, attr.second.get ());
|
||||
pp_string (pp, "\"");
|
||||
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, " />");
|
||||
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);
|
||||
if (indent)
|
||||
child->write_as_xml (pp, depth + 1, indent_children);
|
||||
if (indent_children)
|
||||
{
|
||||
pp_newline (pp);
|
||||
for (int i = 0; i < depth; ++i)
|
||||
pp_string (pp, " ");
|
||||
}
|
||||
pp_printf (pp, "</%s>", m_kind);
|
||||
pp_printf (pp, "</%s>", m_kind.c_str ());
|
||||
}
|
||||
|
||||
if (indent)
|
||||
pp_newline (pp);
|
||||
}
|
||||
|
||||
void
|
||||
element::set_attr (const char *name, label_text value)
|
||||
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;
|
||||
@@ -284,7 +351,8 @@ public:
|
||||
|
||||
html_builder (diagnostic_context &context,
|
||||
pretty_printer &pp,
|
||||
const line_maps *line_maps);
|
||||
const line_maps *line_maps,
|
||||
const html_generation_options &html_gen_opts);
|
||||
|
||||
void on_report_diagnostic (const diagnostic_info &diagnostic,
|
||||
diagnostic_t orig_diag_kind,
|
||||
@@ -309,15 +377,14 @@ public:
|
||||
std::unique_ptr<xml::element>
|
||||
make_element_for_metadata (const diagnostic_metadata &metadata);
|
||||
|
||||
std::unique_ptr<xml::element>
|
||||
make_element_for_source (const diagnostic_info &diagnostic);
|
||||
|
||||
std::unique_ptr<xml::element>
|
||||
make_element_for_path (const diagnostic_path &path);
|
||||
|
||||
std::unique_ptr<xml::element>
|
||||
make_element_for_patch (const diagnostic_info &diagnostic);
|
||||
|
||||
void add_focus_id (std::string focus_id)
|
||||
{
|
||||
m_ui_focus_ids.append_string (focus_id.c_str ());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<xml::element>
|
||||
make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
@@ -330,14 +397,18 @@ private:
|
||||
diagnostic_context &m_context;
|
||||
pretty_printer *m_printer;
|
||||
const line_maps *m_line_maps;
|
||||
html_generation_options m_html_gen_opts;
|
||||
|
||||
std::unique_ptr<xml::document> m_document;
|
||||
xml::element *m_head_element;
|
||||
xml::element *m_diagnostics_element;
|
||||
std::unique_ptr<xml::element> m_cur_diagnostic_element;
|
||||
int m_next_diag_id; // for handing out unique IDs
|
||||
json::array m_ui_focus_ids;
|
||||
};
|
||||
|
||||
static std::unique_ptr<xml::element>
|
||||
make_div (label_text class_)
|
||||
make_div (std::string class_)
|
||||
{
|
||||
auto div = std::make_unique<xml::element> ("div", false);
|
||||
div->set_attr ("class", std::move (class_));
|
||||
@@ -345,7 +416,7 @@ make_div (label_text class_)
|
||||
}
|
||||
|
||||
static std::unique_ptr<xml::element>
|
||||
make_span (label_text class_)
|
||||
make_span (std::string class_)
|
||||
{
|
||||
auto span = std::make_unique<xml::element> ("span", true);
|
||||
span->set_attr ("class", std::move (class_));
|
||||
@@ -400,45 +471,151 @@ diagnostic_html_format_buffer::flush ()
|
||||
|
||||
/* class html_builder. */
|
||||
|
||||
/* Style information for writing out HTML paths.
|
||||
Colors taken from https://www.patternfly.org/v3/styles/color-palette/ */
|
||||
|
||||
static const char * const HTML_STYLE
|
||||
= (" <style>\n"
|
||||
" .linenum { color: white;\n"
|
||||
" background-color: #0088ce;\n"
|
||||
" white-space: pre;\n"
|
||||
" border-right: 1px solid black; }\n"
|
||||
" .ruler { color: red;\n"
|
||||
" white-space: pre; }\n"
|
||||
" .source { color: blue;\n"
|
||||
" white-space: pre; }\n"
|
||||
" .annotation { color: green;\n"
|
||||
" white-space: pre; }\n"
|
||||
" .linenum-gap { text-align: center;\n"
|
||||
" border-top: 1px solid black;\n"
|
||||
" border-right: 1px solid black;\n"
|
||||
" background-color: #ededed; }\n"
|
||||
" .source-gap { border-bottom: 1px dashed black;\n"
|
||||
" border-top: 1px dashed black;\n"
|
||||
" background-color: #ededed; }\n"
|
||||
" .no-locus-event { font-family: monospace;\n"
|
||||
" color: green;\n"
|
||||
" white-space: pre; }\n"
|
||||
" .funcname { font-weight: bold; }\n"
|
||||
" .events-hdr { color: white;\n"
|
||||
" background-color: #030303; }\n"
|
||||
" .event-range { border: 1px solid black;\n"
|
||||
" padding: 0px; }\n"
|
||||
" .event-range-with-margin { border-spacing: 0; }\n"
|
||||
" .locus { font-family: monospace;\n"
|
||||
" border-spacing: 0px; }\n"
|
||||
" .selected { color: white;\n"
|
||||
" background-color: #0088ce; }\n"
|
||||
" .stack-frame-with-margin { border-spacing: 0; }\n"
|
||||
" .stack-frame { padding: 5px;\n"
|
||||
" box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.5); }\n"
|
||||
" .frame-funcname { text-align: right;\n"
|
||||
" font-style: italic; } \n"
|
||||
" </style>\n");
|
||||
|
||||
/* A little JavaScript for ease of navigation.
|
||||
Keys j/k move forward and backward cyclically through a list
|
||||
of focus ids (written out in another <script> tag as the HTML
|
||||
is flushed). */
|
||||
|
||||
const char * const HTML_SCRIPT
|
||||
= (" var current_focus_idx = 0;\n"
|
||||
"\n"
|
||||
" function get_focus_span (focus_idx)\n"
|
||||
" {\n"
|
||||
" const element_id = focus_ids[focus_idx];\n"
|
||||
" return document.getElementById(element_id);\n"
|
||||
" }\n"
|
||||
" function unhighlight_current_focus_idx ()\n"
|
||||
" {\n"
|
||||
" get_focus_span (current_focus_idx).classList.remove ('selected');\n"
|
||||
" }\n"
|
||||
" function highlight_current_focus_idx ()\n"
|
||||
" {\n"
|
||||
" const el = get_focus_span (current_focus_idx);\n"
|
||||
" el.classList.add ('selected');\n"
|
||||
" // Center the element on the screen\n"
|
||||
" const top_y = el.getBoundingClientRect ().top + window.pageYOffset;\n"
|
||||
" const middle = top_y - (window.innerHeight / 2);\n"
|
||||
" window.scrollTo (0, middle);\n"
|
||||
" }\n"
|
||||
" function select_prev_focus_idx ()\n"
|
||||
" {\n"
|
||||
" unhighlight_current_focus_idx ();\n"
|
||||
" if (current_focus_idx > 0)\n"
|
||||
" current_focus_idx -= 1;\n"
|
||||
" else\n"
|
||||
" current_focus_idx = focus_ids.length - 1;\n"
|
||||
" highlight_current_focus_idx ();\n"
|
||||
" }\n"
|
||||
" function select_next_focus_idx ()\n"
|
||||
" {\n"
|
||||
" unhighlight_current_focus_idx ();\n"
|
||||
" if (current_focus_idx < focus_ids.length - 1)\n"
|
||||
" current_focus_idx += 1;\n"
|
||||
" else\n"
|
||||
" current_focus_idx = 0;\n"
|
||||
" highlight_current_focus_idx ();\n"
|
||||
" }\n"
|
||||
" document.addEventListener('keydown', function (ev) {\n"
|
||||
" if (ev.key == 'j')\n"
|
||||
" select_next_focus_idx ();\n"
|
||||
" else if (ev.key == 'k')\n"
|
||||
" select_prev_focus_idx ();\n"
|
||||
" });\n"
|
||||
" highlight_current_focus_idx ();\n");
|
||||
|
||||
/* html_builder's ctor. */
|
||||
|
||||
html_builder::html_builder (diagnostic_context &context,
|
||||
pretty_printer &pp,
|
||||
const line_maps *line_maps)
|
||||
pretty_printer &pp,
|
||||
const line_maps *line_maps,
|
||||
const html_generation_options &html_gen_opts)
|
||||
: m_context (context),
|
||||
m_printer (&pp),
|
||||
m_line_maps (line_maps)
|
||||
m_line_maps (line_maps),
|
||||
m_html_gen_opts (html_gen_opts),
|
||||
m_head_element (nullptr),
|
||||
m_diagnostics_element (nullptr),
|
||||
m_next_diag_id (0)
|
||||
{
|
||||
gcc_assert (m_line_maps);
|
||||
|
||||
m_document = std::make_unique<xml::document> ();
|
||||
{
|
||||
auto html_element = std::make_unique<xml::element> ("html", false);
|
||||
html_element->set_attr
|
||||
("xmlns",
|
||||
label_text::borrow ("http://www.w3.org/1999/xhtml"));
|
||||
{
|
||||
{
|
||||
auto head_element = std::make_unique<xml::element> ("head", false);
|
||||
{
|
||||
auto title_element = std::make_unique<xml::element> ("title", true);
|
||||
label_text title (label_text::borrow ("Title goes here")); // TODO
|
||||
title_element->add_text (std::move (title));
|
||||
head_element->add_child (std::move (title_element));
|
||||
}
|
||||
html_element->add_child (std::move (head_element));
|
||||
html_element->set_attr ("xmlns",
|
||||
"http://www.w3.org/1999/xhtml");
|
||||
xml::printer xp (*html_element.get ());
|
||||
m_document->add_child (std::move (html_element));
|
||||
|
||||
auto body_element = std::make_unique<xml::element> ("body", false);
|
||||
{
|
||||
xml::auto_print_element head (xp, "head");
|
||||
m_head_element = xp.get_insertion_point ();
|
||||
{
|
||||
xml::auto_print_element title (xp, "title", true);
|
||||
xp.add_text ("Title goes here");
|
||||
}
|
||||
if (m_html_gen_opts.m_css)
|
||||
xp.add_raw (HTML_STYLE);
|
||||
if (m_html_gen_opts.m_javascript)
|
||||
{
|
||||
auto diagnostics_element
|
||||
= make_div (label_text::borrow ("gcc-diagnostic-list"));
|
||||
m_diagnostics_element = diagnostics_element.get ();
|
||||
body_element->add_child (std::move (diagnostics_element));
|
||||
xp.push_tag ("script");
|
||||
/* Escaping rules are different for HTML <script> elements,
|
||||
so add the script "raw" for now. */
|
||||
xp.add_raw (HTML_SCRIPT);
|
||||
xp.pop_tag (); // script
|
||||
}
|
||||
html_element->add_child (std::move (body_element));
|
||||
}
|
||||
|
||||
{
|
||||
xml::auto_print_element body (xp, "body");
|
||||
{
|
||||
auto diagnostics_element = make_div ("gcc-diagnostic-list");
|
||||
m_diagnostics_element = diagnostics_element.get ();
|
||||
xp.append (std::move (diagnostics_element));
|
||||
}
|
||||
}
|
||||
m_document->add_child (std::move (html_element));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,6 +654,45 @@ html_builder::on_report_diagnostic (const diagnostic_info &diagnostic,
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom subclass of html_label_writer.
|
||||
Wrap labels within a <span> element, supplying them with event IDs.
|
||||
Add the IDs to the list of focus IDs. */
|
||||
|
||||
class html_path_label_writer : public html_label_writer
|
||||
{
|
||||
public:
|
||||
html_path_label_writer (xml::printer &xp,
|
||||
html_builder &builder,
|
||||
const std::string &event_id_prefix)
|
||||
: m_xp (xp),
|
||||
m_html_builder (builder),
|
||||
m_event_id_prefix (event_id_prefix),
|
||||
m_next_event_idx (0)
|
||||
{
|
||||
}
|
||||
|
||||
void begin_label () final override
|
||||
{
|
||||
m_xp.push_tag_with_class ("span", "event", true);
|
||||
pretty_printer pp;
|
||||
pp_printf (&pp, "%s%i",
|
||||
m_event_id_prefix.c_str (), m_next_event_idx++);
|
||||
m_xp.set_attr ("id", pp_formatted_text (&pp));
|
||||
m_html_builder.add_focus_id (pp_formatted_text (&pp));
|
||||
}
|
||||
|
||||
void end_label () final override
|
||||
{
|
||||
m_xp.pop_tag (); // span
|
||||
}
|
||||
|
||||
private:
|
||||
xml::printer &m_xp;
|
||||
html_builder &m_html_builder;
|
||||
const std::string &m_event_id_prefix;
|
||||
int m_next_event_idx;
|
||||
};
|
||||
|
||||
std::unique_ptr<xml::element>
|
||||
html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
diagnostic_t orig_diag_kind)
|
||||
@@ -506,8 +722,7 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
pp_token_text *sub = as_a <pp_token_text *> (iter);
|
||||
/* The value might be in the obstack, so we may need to
|
||||
copy it. */
|
||||
insertion_element ().add_text
|
||||
(label_text::take (xstrdup (sub->m_value.get ())));
|
||||
insertion_element ().add_text (sub->m_value.get ());
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -518,14 +733,14 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
|
||||
case pp_token::kind::begin_quote:
|
||||
{
|
||||
insertion_element ().add_text (label_text::borrow (open_quote));
|
||||
push_element (make_span (label_text::borrow ("gcc-quoted-text")));
|
||||
insertion_element ().add_text (open_quote);
|
||||
push_element (make_span ("gcc-quoted-text"));
|
||||
}
|
||||
break;
|
||||
case pp_token::kind::end_quote:
|
||||
{
|
||||
pop_element ();
|
||||
insertion_element ().add_text (label_text::borrow (close_quote));
|
||||
insertion_element ().add_text (close_quote);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -533,7 +748,7 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
{
|
||||
pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);
|
||||
auto anchor = std::make_unique<xml::element> ("a", true);
|
||||
anchor->set_attr ("href", std::move (sub->m_value));
|
||||
anchor->set_attr ("href", sub->m_value.get ());
|
||||
push_element (std::move (anchor));
|
||||
}
|
||||
break;
|
||||
@@ -565,11 +780,24 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
std::vector<xml::element *> m_open_elements;
|
||||
};
|
||||
|
||||
auto diag_element = make_div (label_text::borrow ("gcc-diagnostic"));
|
||||
auto diag_element = make_div ("gcc-diagnostic");
|
||||
|
||||
const int diag_idx = m_next_diag_id++;
|
||||
std::string diag_id;
|
||||
{
|
||||
pretty_printer pp;
|
||||
pp_printf (&pp, "gcc-diag-%i", diag_idx);
|
||||
diag_id = pp_formatted_text (&pp);
|
||||
}
|
||||
diag_element->set_attr ("id", diag_id);
|
||||
|
||||
// TODO: might be nice to emulate the text output format, but colorize it
|
||||
|
||||
auto message_span = make_span (label_text::borrow ("gcc-message"));
|
||||
auto message_span = make_span ("gcc-message");
|
||||
std::string message_span_id (diag_id + "-message");
|
||||
message_span->set_attr ("id", message_span_id);
|
||||
add_focus_id (message_span_id);
|
||||
|
||||
html_token_printer tok_printer (*this, *message_span.get ());
|
||||
m_printer->set_token_printer (&tok_printer);
|
||||
pp_output_formatted_text (m_printer, m_context.get_urlifier ());
|
||||
@@ -579,7 +807,7 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
|
||||
if (diagnostic.metadata)
|
||||
{
|
||||
diag_element->add_text (label_text::borrow (" "));
|
||||
diag_element->add_text (" ");
|
||||
diag_element->add_child
|
||||
(make_element_for_metadata (*diagnostic.metadata));
|
||||
}
|
||||
@@ -592,32 +820,47 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
label_text option_url = label_text::take
|
||||
(m_context.make_option_url (diagnostic.option_id));
|
||||
|
||||
diag_element->add_text (label_text::borrow (" "));
|
||||
auto option_span = make_span (label_text::borrow ("gcc-option"));
|
||||
option_span->add_text (label_text::borrow ("["));
|
||||
diag_element->add_text (" ");
|
||||
auto option_span = make_span ("gcc-option");
|
||||
option_span->add_text ("[");
|
||||
{
|
||||
if (option_url.get ())
|
||||
{
|
||||
auto anchor = std::make_unique<xml::element> ("a", true);
|
||||
anchor->set_attr ("href", std::move (option_url));
|
||||
anchor->add_text (std::move (option_text));
|
||||
anchor->set_attr ("href", option_url.get ());
|
||||
anchor->add_text (option_text.get ());
|
||||
option_span->add_child (std::move (anchor));
|
||||
}
|
||||
else
|
||||
option_span->add_text (std::move (option_text));
|
||||
option_span->add_text (label_text::borrow ("]"));
|
||||
option_span->add_text (option_text.get ());
|
||||
option_span->add_text ("]");
|
||||
}
|
||||
diag_element->add_child (std::move (option_span));
|
||||
}
|
||||
|
||||
/* Source (and fix-it hints). */
|
||||
if (auto source_element = make_element_for_source (diagnostic))
|
||||
diag_element->add_child (std::move (source_element));
|
||||
{
|
||||
xml::printer xp (*diag_element);
|
||||
m_context.m_last_location = UNKNOWN_LOCATION;
|
||||
m_context.maybe_show_locus_as_html (*diagnostic.richloc,
|
||||
m_context.m_source_printing,
|
||||
diagnostic.kind,
|
||||
xp,
|
||||
nullptr,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
/* Execution path. */
|
||||
if (auto path = diagnostic.richloc->get_path ())
|
||||
if (auto path_element = make_element_for_path (*path))
|
||||
diag_element->add_child (std::move (path_element));
|
||||
{
|
||||
xml::printer xp (*diag_element);
|
||||
std::string event_id_prefix (diag_id + "-event-");
|
||||
html_path_label_writer event_label_writer (xp, *this,
|
||||
event_id_prefix);
|
||||
diagnostic_source_print_policy dspp (m_context);
|
||||
print_path_as_html (xp, *path, m_context, &event_label_writer,
|
||||
dspp);
|
||||
}
|
||||
|
||||
if (auto patch_element = make_element_for_patch (diagnostic))
|
||||
diag_element->add_child (std::move (patch_element));
|
||||
@@ -625,62 +868,24 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
return diag_element;
|
||||
}
|
||||
|
||||
std::unique_ptr<xml::element>
|
||||
html_builder::make_element_for_source (const diagnostic_info &diagnostic)
|
||||
{
|
||||
// TODO: ideally we'd like to capture elements within the following:
|
||||
m_context.m_last_location = UNKNOWN_LOCATION;
|
||||
pp_clear_output_area (m_printer);
|
||||
diagnostic_show_locus (&m_context,
|
||||
m_context.m_source_printing,
|
||||
diagnostic.richloc, diagnostic.kind,
|
||||
m_printer);
|
||||
auto text = label_text::take (xstrdup (pp_formatted_text (m_printer)));
|
||||
pp_clear_output_area (m_printer);
|
||||
|
||||
if (strlen (text.get ()) == 0)
|
||||
return nullptr;
|
||||
|
||||
auto pre = std::make_unique<xml::element> ("pre", true);
|
||||
pre->set_attr ("class", label_text::borrow ("gcc-annotated-source"));
|
||||
pre->add_text (std::move (text));
|
||||
return pre;
|
||||
}
|
||||
|
||||
std::unique_ptr<xml::element>
|
||||
html_builder::make_element_for_path (const diagnostic_path &path)
|
||||
{
|
||||
m_context.m_last_location = UNKNOWN_LOCATION;
|
||||
diagnostic_text_output_format text_format (m_context);
|
||||
pp_show_color (text_format.get_printer ()) = false;
|
||||
pp_buffer (text_format.get_printer ())->m_flush_p = false;
|
||||
// TODO: ideally we'd like to capture elements within the following:
|
||||
text_format.print_path (path);
|
||||
auto text = label_text::take
|
||||
(xstrdup (pp_formatted_text (text_format.get_printer ())));
|
||||
|
||||
if (strlen (text.get ()) == 0)
|
||||
return nullptr;
|
||||
|
||||
auto pre = std::make_unique<xml::element> ("pre", true);
|
||||
pre->set_attr ("class", label_text::borrow ("gcc-execution-path"));
|
||||
pre->add_text (std::move (text));
|
||||
return pre;
|
||||
}
|
||||
|
||||
std::unique_ptr<xml::element>
|
||||
html_builder::make_element_for_patch (const diagnostic_info &diagnostic)
|
||||
{
|
||||
edit_context ec (m_context.get_file_cache ());
|
||||
ec.add_fixits (diagnostic.richloc);
|
||||
if (char *diff = ec.generate_diff (true))
|
||||
if (strlen (diff) > 0)
|
||||
{
|
||||
auto element = std::make_unique<xml::element> ("pre", true);
|
||||
element->set_attr ("class", label_text::borrow ("gcc-generated-patch"));
|
||||
element->add_text (label_text::take (diff));
|
||||
return element;
|
||||
}
|
||||
{
|
||||
if (strlen (diff) > 0)
|
||||
{
|
||||
auto element = std::make_unique<xml::element> ("pre", true);
|
||||
element->set_attr ("class", "gcc-generated-patch");
|
||||
element->add_text (diff);
|
||||
free (diff);
|
||||
return element;
|
||||
}
|
||||
else
|
||||
free (diff);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -688,22 +893,23 @@ std::unique_ptr<xml::element>
|
||||
html_builder::make_metadata_element (label_text label,
|
||||
label_text url)
|
||||
{
|
||||
auto item = make_span (label_text::borrow ("gcc-metadata-item"));
|
||||
item->add_text (label_text::borrow ("["));
|
||||
auto item = make_span ("gcc-metadata-item");
|
||||
xml::printer xp (*item.get ());
|
||||
xp.add_text ("[");
|
||||
{
|
||||
auto anchor = std::make_unique<xml::element> ("a", true);
|
||||
anchor->set_attr ("href", std::move (url));
|
||||
anchor->add_child (std::make_unique<xml::text> (std::move (label)));
|
||||
item->add_child (std::move (anchor));
|
||||
xp.push_tag ("a", true);
|
||||
xp.set_attr ("href", url.get ());
|
||||
xp.add_text (label.get ());
|
||||
xp.pop_tag ();
|
||||
}
|
||||
item->add_text (label_text::borrow ("]"));
|
||||
xp.add_text ("]");
|
||||
return item;
|
||||
}
|
||||
|
||||
std::unique_ptr<xml::element>
|
||||
html_builder::make_element_for_metadata (const diagnostic_metadata &metadata)
|
||||
{
|
||||
auto span_metadata = make_span (label_text::borrow ("gcc-metadata"));
|
||||
auto span_metadata = make_span ("gcc-metadata");
|
||||
|
||||
int cwe = metadata.get_cwe ();
|
||||
if (cwe)
|
||||
@@ -737,7 +943,7 @@ html_builder::emit_diagram (const diagnostic_diagram &/*diagram*/)
|
||||
/* We must be within the emission of a top-level diagnostic. */
|
||||
gcc_assert (m_cur_diagnostic_element);
|
||||
|
||||
// TODO
|
||||
// TODO: currently a no-op
|
||||
}
|
||||
|
||||
/* Implementation of "end_group_cb" for HTML output. */
|
||||
@@ -757,6 +963,20 @@ html_builder::end_group ()
|
||||
void
|
||||
html_builder::flush_to_file (FILE *outf)
|
||||
{
|
||||
if (m_html_gen_opts.m_javascript)
|
||||
{
|
||||
gcc_assert (m_head_element);
|
||||
xml::printer xp (*m_head_element);
|
||||
/* Add an initialization of the global js variable "focus_ids"
|
||||
using the array of IDs we saved as we went. */
|
||||
xp.push_tag ("script");
|
||||
pretty_printer pp;
|
||||
pp_string (&pp, "focus_ids = ");
|
||||
m_ui_focus_ids.print (&pp, true);
|
||||
pp_string (&pp, ";\n");
|
||||
xp.add_raw (pp_formatted_text (&pp));
|
||||
xp.pop_tag (); // script
|
||||
}
|
||||
auto top = m_document.get ();
|
||||
top->dump (outf);
|
||||
fprintf (outf, "\n");
|
||||
@@ -842,9 +1062,10 @@ public:
|
||||
|
||||
protected:
|
||||
html_output_format (diagnostic_context &context,
|
||||
const line_maps *line_maps)
|
||||
const line_maps *line_maps,
|
||||
const html_generation_options &html_gen_opts)
|
||||
: diagnostic_output_format (context),
|
||||
m_builder (context, *get_printer (), line_maps),
|
||||
m_builder (context, *get_printer (), line_maps, html_gen_opts),
|
||||
m_buffer (nullptr)
|
||||
{}
|
||||
|
||||
@@ -857,8 +1078,9 @@ class html_file_output_format : public html_output_format
|
||||
public:
|
||||
html_file_output_format (diagnostic_context &context,
|
||||
const line_maps *line_maps,
|
||||
const html_generation_options &html_gen_opts,
|
||||
diagnostic_output_file output_file)
|
||||
: html_output_format (context, line_maps),
|
||||
: html_output_format (context, line_maps, html_gen_opts),
|
||||
m_output_file (std::move (output_file))
|
||||
{
|
||||
gcc_assert (m_output_file.get_open_file ());
|
||||
@@ -922,11 +1144,13 @@ diagnostic_output_format_open_html_file (diagnostic_context &context,
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_html_sink (diagnostic_context &context,
|
||||
const line_maps &line_maps,
|
||||
const html_generation_options &html_gen_opts,
|
||||
diagnostic_output_file output_file)
|
||||
{
|
||||
auto sink
|
||||
= std::make_unique<html_file_output_format> (context,
|
||||
&line_maps,
|
||||
html_gen_opts,
|
||||
std::move (output_file));
|
||||
sink->update_printer ();
|
||||
return sink;
|
||||
@@ -945,8 +1169,12 @@ class test_html_diagnostic_context : public test_diagnostic_context
|
||||
public:
|
||||
test_html_diagnostic_context ()
|
||||
{
|
||||
html_generation_options html_gen_opts;
|
||||
html_gen_opts.m_css = false;
|
||||
html_gen_opts.m_javascript = false;
|
||||
auto sink = std::make_unique<html_buffered_output_format> (*this,
|
||||
line_table);
|
||||
line_table,
|
||||
html_gen_opts);
|
||||
sink->update_printer ();
|
||||
m_format = sink.get (); // borrowed
|
||||
|
||||
@@ -968,8 +1196,9 @@ private:
|
||||
{
|
||||
public:
|
||||
html_buffered_output_format (diagnostic_context &context,
|
||||
const line_maps *line_maps)
|
||||
: html_output_format (context, line_maps)
|
||||
const line_maps *line_maps,
|
||||
const html_generation_options &html_gen_opts)
|
||||
: html_output_format (context, line_maps, html_gen_opts)
|
||||
{
|
||||
}
|
||||
bool machine_readable_stderr_p () const final override
|
||||
@@ -1009,12 +1238,12 @@ test_simple_log ()
|
||||
" </head>\n"
|
||||
" <body>\n"
|
||||
" <div class=\"gcc-diagnostic-list\">\n"
|
||||
" <div class=\"gcc-diagnostic\">\n"
|
||||
" <span class=\"gcc-message\">this is a test: `<span class=\"gcc-quoted-text\">foo</span>'</span>\n"
|
||||
" <div class=\"gcc-diagnostic\" id=\"gcc-diag-0\">\n"
|
||||
" <span class=\"gcc-message\" id=\"gcc-diag-0-message\">this is a test: `<span class=\"gcc-quoted-text\">foo</span>'</span>\n"
|
||||
" </div>\n"
|
||||
" </div>\n"
|
||||
" </body>\n"
|
||||
"</html>"));
|
||||
"</html>\n"));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1031,7 +1260,6 @@ test_metadata ()
|
||||
element->write_as_xml (&pp, 0, true);
|
||||
ASSERT_STREQ
|
||||
(pp_formatted_text (&pp),
|
||||
"\n"
|
||||
"<span class=\"gcc-metadata\">"
|
||||
"<span class=\"gcc-metadata-item\">"
|
||||
"["
|
||||
@@ -1040,7 +1268,7 @@ test_metadata ()
|
||||
"</a>"
|
||||
"]"
|
||||
"</span>"
|
||||
"</span>");
|
||||
"</span>\n");
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1053,7 +1281,6 @@ test_metadata ()
|
||||
element->write_as_xml (&pp, 0, true);
|
||||
ASSERT_STREQ
|
||||
(pp_formatted_text (&pp),
|
||||
"\n"
|
||||
"<span class=\"gcc-metadata\">"
|
||||
"<span class=\"gcc-metadata-item\">"
|
||||
"["
|
||||
@@ -1062,10 +1289,71 @@ test_metadata ()
|
||||
"</a>"
|
||||
"]"
|
||||
"</span>"
|
||||
"</span>");
|
||||
"</span>\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
|
||||
@@ -1074,6 +1362,8 @@ diagnostic_format_html_cc_tests ()
|
||||
auto_fix_quotes fix_quotes;
|
||||
test_simple_log ();
|
||||
test_metadata ();
|
||||
test_printer ();
|
||||
test_attribute_ordering ();
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
@@ -24,6 +24,14 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "diagnostic-format.h"
|
||||
#include "diagnostic-output-file.h"
|
||||
|
||||
struct html_generation_options
|
||||
{
|
||||
html_generation_options ();
|
||||
|
||||
bool m_css;
|
||||
bool m_javascript;
|
||||
};
|
||||
|
||||
extern diagnostic_output_file
|
||||
diagnostic_output_format_open_html_file (diagnostic_context &context,
|
||||
line_maps *line_maps,
|
||||
@@ -32,6 +40,14 @@ diagnostic_output_format_open_html_file (diagnostic_context &context,
|
||||
extern std::unique_ptr<diagnostic_output_format>
|
||||
make_html_sink (diagnostic_context &context,
|
||||
const line_maps &line_maps,
|
||||
const html_generation_options &html_gen_opts,
|
||||
diagnostic_output_file output_file);
|
||||
|
||||
extern void
|
||||
print_path_as_html (xml::printer &xp,
|
||||
const diagnostic_path &path,
|
||||
diagnostic_context &dc,
|
||||
html_label_writer *event_label_writer,
|
||||
const diagnostic_source_print_policy &dspp);
|
||||
|
||||
#endif /* ! GCC_DIAGNOSTIC_FORMAT_HTML_H */
|
||||
|
||||
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
|
||||
#include "config.h"
|
||||
#define INCLUDE_ALGORITHM
|
||||
#define INCLUDE_MAP
|
||||
#define INCLUDE_STRING
|
||||
#define INCLUDE_VECTOR
|
||||
#include "system.h"
|
||||
@@ -38,6 +39,9 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "selftest-diagnostic-path.h"
|
||||
#include "text-art/theme.h"
|
||||
#include "diagnostic-format-text.h"
|
||||
#include "diagnostic-format-html.h"
|
||||
#include "xml.h"
|
||||
#include "xml-printer.h"
|
||||
|
||||
/* Disable warnings about missing quoting in GCC diagnostics for the print
|
||||
calls below. */
|
||||
@@ -60,10 +64,15 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
path_print_policy (const diagnostic_context &dc)
|
||||
: m_source_policy (dc)
|
||||
{
|
||||
}
|
||||
|
||||
text_art::theme *
|
||||
get_diagram_theme () const
|
||||
{
|
||||
return m_source_policy.get_diagram_theme ();
|
||||
return m_source_policy.get_diagram_theme ();
|
||||
}
|
||||
|
||||
const diagnostic_source_print_policy &
|
||||
@@ -276,6 +285,119 @@ private:
|
||||
int m_max_depth;
|
||||
};
|
||||
|
||||
/* A stack frame for use in HTML output, holding child stack frames,
|
||||
and event ranges. */
|
||||
|
||||
struct stack_frame
|
||||
{
|
||||
stack_frame (std::unique_ptr<stack_frame> parent,
|
||||
logical_location logical_loc,
|
||||
int stack_depth)
|
||||
: m_parent (std::move (parent)),
|
||||
m_logical_loc (logical_loc),
|
||||
m_stack_depth (stack_depth)
|
||||
{}
|
||||
|
||||
std::unique_ptr<stack_frame> m_parent;
|
||||
logical_location m_logical_loc;
|
||||
const int m_stack_depth;
|
||||
};
|
||||
|
||||
/* Begin emitting content relating to a new stack frame within PARENT.
|
||||
Allocated a new stack_frame and return it. */
|
||||
|
||||
static std::unique_ptr<stack_frame>
|
||||
begin_html_stack_frame (xml::printer &xp,
|
||||
std::unique_ptr<stack_frame> parent,
|
||||
logical_location logical_loc,
|
||||
int stack_depth,
|
||||
const logical_location_manager *logical_loc_mgr)
|
||||
{
|
||||
if (logical_loc)
|
||||
{
|
||||
gcc_assert (logical_loc_mgr);
|
||||
xp.push_tag_with_class ("table", "stack-frame-with-margin", false);
|
||||
xp.push_tag ("tr", false);
|
||||
{
|
||||
xp.push_tag_with_class ("td", "interprocmargin", false);
|
||||
xp.set_attr ("style", "padding-left: 100px");
|
||||
xp.pop_tag ();
|
||||
}
|
||||
xp.push_tag_with_class ("td", "stack-frame", false);
|
||||
label_text funcname
|
||||
= logical_loc_mgr->get_name_for_path_output (logical_loc);
|
||||
if (funcname.get ())
|
||||
{
|
||||
xp.push_tag_with_class ("div", "frame-funcname", false);
|
||||
xp.push_tag ("span", true);
|
||||
xp.add_text (funcname.get ());
|
||||
xp.pop_tag (); // span
|
||||
xp.pop_tag (); // div
|
||||
}
|
||||
}
|
||||
return std::make_unique<stack_frame> (std::move (parent),
|
||||
logical_loc,
|
||||
stack_depth);
|
||||
}
|
||||
|
||||
/* Finish emitting content for FRAME and delete it.
|
||||
Return parent. */
|
||||
|
||||
static std::unique_ptr<stack_frame>
|
||||
end_html_stack_frame (xml::printer &xp,
|
||||
std::unique_ptr<stack_frame> frame)
|
||||
{
|
||||
auto parent = std::move (frame->m_parent);
|
||||
if (frame->m_logical_loc)
|
||||
{
|
||||
xp.pop_tag (); // td
|
||||
xp.pop_tag (); // tr
|
||||
xp.pop_tag (); // table
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
/* Append an HTML <div> element to XP containing an SVG arrow representing
|
||||
a change in stack depth from OLD_DEPTH to NEW_DEPTH. */
|
||||
|
||||
static void
|
||||
emit_svg_arrow (xml::printer &xp, int old_depth, int new_depth)
|
||||
{
|
||||
const int pixels_per_depth = 100;
|
||||
const int min_depth = MIN (old_depth, new_depth);
|
||||
const int base_x = 20;
|
||||
const int excess = 30;
|
||||
const int last_x
|
||||
= base_x + (old_depth - min_depth) * pixels_per_depth;
|
||||
const int this_x
|
||||
= base_x + (new_depth - min_depth) * pixels_per_depth;
|
||||
pretty_printer tmp_pp;
|
||||
pretty_printer *pp = &tmp_pp;
|
||||
pp_printf (pp, "<div class=\"%s\">\n",
|
||||
old_depth < new_depth
|
||||
? "between-ranges-call" : "between-ranges-return");
|
||||
pp_printf (pp, " <svg height=\"30\" width=\"%i\">\n",
|
||||
MAX (last_x, this_x) + excess);
|
||||
pp_string
|
||||
(pp,
|
||||
" <defs>\n"
|
||||
" <marker id=\"arrowhead\" markerWidth=\"10\" markerHeight=\"7\"\n"
|
||||
" refX=\"0\" refY=\"3.5\" orient=\"auto\" stroke=\"#0088ce\" fill=\"#0088ce\">\n"
|
||||
" <polygon points=\"0 0, 10 3.5, 0 7\"/>\n"
|
||||
" </marker>\n"
|
||||
" </defs>\n");
|
||||
pp_printf (pp,
|
||||
" <polyline points=\"%i,0 %i,10 %i,10 %i,20\"\n",
|
||||
last_x, last_x, this_x, this_x);
|
||||
pp_string
|
||||
(pp,
|
||||
" style=\"fill:none;stroke: #0088ce\"\n"
|
||||
" marker-end=\"url(#arrowhead)\"/>\n"
|
||||
" </svg>\n"
|
||||
"</div>\n\n");
|
||||
xp.add_raw (pp_formatted_text (pp));
|
||||
}
|
||||
|
||||
/* A range of consecutive events within a diagnostic_path, all within the
|
||||
same thread, and with the same fndecl and stack_depth, and which are suitable
|
||||
to print with a single call to diagnostic_show_locus. */
|
||||
@@ -468,9 +590,9 @@ struct event_range
|
||||
/* Print the events in this range to PP, typically as a single
|
||||
call to diagnostic_show_locus. */
|
||||
|
||||
void print (pretty_printer &pp,
|
||||
diagnostic_text_output_format &text_output,
|
||||
diagnostic_source_effect_info *effect_info)
|
||||
void print_as_text (pretty_printer &pp,
|
||||
diagnostic_text_output_format &text_output,
|
||||
diagnostic_source_effect_info *effect_info)
|
||||
{
|
||||
location_t initial_loc = m_initial_event.get_location ();
|
||||
|
||||
@@ -487,7 +609,7 @@ struct event_range
|
||||
if (exploc.file != LOCATION_FILE (dc.m_last_location))
|
||||
{
|
||||
diagnostic_location_print_policy loc_policy (text_output);
|
||||
diagnostic_start_span (&dc) (loc_policy, &pp, exploc);
|
||||
loc_policy.print_text_span_start (dc, pp, exploc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,6 +646,65 @@ struct event_range
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the events in this range to XP, typically as a single
|
||||
call to diagnostic_show_locus_as_html. */
|
||||
|
||||
void print_as_html (xml::printer &xp,
|
||||
diagnostic_context &dc,
|
||||
diagnostic_source_effect_info *effect_info,
|
||||
html_label_writer *event_label_writer)
|
||||
{
|
||||
location_t initial_loc = m_initial_event.get_location ();
|
||||
|
||||
/* Emit a span indicating the filename (and line/column) if the
|
||||
line has changed relative to the last call to
|
||||
diagnostic_show_locus. */
|
||||
if (dc.m_source_printing.enabled)
|
||||
{
|
||||
expanded_location exploc
|
||||
= linemap_client_expand_location_to_spelling_point
|
||||
(line_table, initial_loc, LOCATION_ASPECT_CARET);
|
||||
if (exploc.file != LOCATION_FILE (dc.m_last_location))
|
||||
{
|
||||
diagnostic_location_print_policy loc_policy (dc);
|
||||
loc_policy.print_html_span_start (dc, xp, exploc);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
|
||||
primary location for an event, diagnostic_show_locus_as_html won't print
|
||||
anything.
|
||||
|
||||
In particular the label for the event won't get printed.
|
||||
Fail more gracefully in this case by showing the event
|
||||
index and text, at no particular location. */
|
||||
if (get_pure_location (initial_loc) <= BUILTINS_LOCATION)
|
||||
{
|
||||
for (unsigned i = m_start_idx; i <= m_end_idx; i++)
|
||||
{
|
||||
const diagnostic_event &iter_event = m_path.get_event (i);
|
||||
diagnostic_event_id_t event_id (i);
|
||||
pretty_printer pp;
|
||||
pp_printf (&pp, " %@: ", &event_id);
|
||||
iter_event.print_desc (pp);
|
||||
if (event_label_writer)
|
||||
event_label_writer->begin_label ();
|
||||
xp.add_text (pp_formatted_text (&pp));
|
||||
if (event_label_writer)
|
||||
event_label_writer->end_label ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Call diagnostic_show_locus_as_html to show the source,
|
||||
showing events using labels. */
|
||||
diagnostic_show_locus_as_html (&dc, dc.m_source_printing,
|
||||
&m_richloc, DK_DIAGNOSTIC_PATH, xp,
|
||||
effect_info, event_label_writer);
|
||||
|
||||
// TODO: show macro expansions
|
||||
}
|
||||
|
||||
const diagnostic_path &m_path;
|
||||
const diagnostic_event &m_initial_event;
|
||||
logical_location m_logical_loc;
|
||||
@@ -700,11 +881,11 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
print_swimlane_for_event_range (diagnostic_text_output_format &text_output,
|
||||
pretty_printer *pp,
|
||||
const logical_location_manager &logical_loc_mgr,
|
||||
event_range *range,
|
||||
diagnostic_source_effect_info *effect_info)
|
||||
print_swimlane_for_event_range_as_text (diagnostic_text_output_format &text_output,
|
||||
pretty_printer *pp,
|
||||
const logical_location_manager &logical_loc_mgr,
|
||||
event_range *range,
|
||||
diagnostic_source_effect_info *effect_info)
|
||||
{
|
||||
gcc_assert (pp);
|
||||
const char *const line_color = "path";
|
||||
@@ -785,7 +966,7 @@ public:
|
||||
}
|
||||
pp_set_prefix (pp, prefix);
|
||||
pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
|
||||
range->print (*pp, text_output, effect_info);
|
||||
range->print_as_text (*pp, text_output, effect_info);
|
||||
pp_set_prefix (pp, saved_prefix);
|
||||
|
||||
write_indent (pp, m_cur_indent + per_frame_indent);
|
||||
@@ -795,7 +976,7 @@ public:
|
||||
pp_newline (pp);
|
||||
}
|
||||
else
|
||||
range->print (*pp, text_output, effect_info);
|
||||
range->print_as_text (*pp, text_output, effect_info);
|
||||
|
||||
if (const event_range *next_range = get_any_next_range ())
|
||||
{
|
||||
@@ -859,6 +1040,17 @@ public:
|
||||
m_num_printed++;
|
||||
}
|
||||
|
||||
void
|
||||
print_swimlane_for_event_range_as_html (diagnostic_context &dc,
|
||||
xml::printer &xp,
|
||||
html_label_writer *event_label_writer,
|
||||
event_range *range,
|
||||
diagnostic_source_effect_info *effect_info)
|
||||
{
|
||||
range->print_as_html (xp, dc, effect_info, event_label_writer);
|
||||
m_num_printed++;
|
||||
}
|
||||
|
||||
int get_cur_indent () const { return m_cur_indent; }
|
||||
|
||||
private:
|
||||
@@ -945,13 +1137,148 @@ print_path_summary_as_text (const path_summary &ps,
|
||||
of this range. */
|
||||
diagnostic_source_effect_info effect_info;
|
||||
effect_info.m_leading_in_edge_column = last_out_edge_column;
|
||||
tep.print_swimlane_for_event_range (text_output, pp,
|
||||
ps.get_logical_location_manager (),
|
||||
range, &effect_info);
|
||||
tep.print_swimlane_for_event_range_as_text
|
||||
(text_output, pp,
|
||||
ps.get_logical_location_manager (),
|
||||
range, &effect_info);
|
||||
last_out_edge_column = effect_info.m_trailing_out_edge_column;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print PS as HTML to XP, using DC and, if non-null EVENT_LABEL_WRITER. */
|
||||
|
||||
static void
|
||||
print_path_summary_as_html (const path_summary &ps,
|
||||
diagnostic_context &dc,
|
||||
xml::printer &xp,
|
||||
html_label_writer *event_label_writer,
|
||||
bool show_depths)
|
||||
{
|
||||
std::vector<thread_event_printer> thread_event_printers;
|
||||
for (auto t : ps.m_per_thread_summary)
|
||||
thread_event_printers.push_back (thread_event_printer (*t, show_depths));
|
||||
|
||||
const logical_location_manager *logical_loc_mgr
|
||||
= dc.get_logical_location_manager ();
|
||||
|
||||
xp.push_tag_with_class ("div", "event-ranges", false);
|
||||
|
||||
/* Group the ranges into stack frames. */
|
||||
std::unique_ptr<stack_frame> curr_frame;
|
||||
unsigned i;
|
||||
event_range *range;
|
||||
int last_out_edge_column = -1;
|
||||
FOR_EACH_VEC_ELT (ps.m_ranges, i, range)
|
||||
{
|
||||
const int swimlane_idx
|
||||
= range->m_per_thread_summary.get_swimlane_index ();
|
||||
|
||||
const logical_location this_logical_loc = range->m_logical_loc;
|
||||
const int this_depth = range->m_stack_depth;
|
||||
if (curr_frame)
|
||||
{
|
||||
int old_stack_depth = curr_frame->m_stack_depth;
|
||||
if (this_depth > curr_frame->m_stack_depth)
|
||||
{
|
||||
emit_svg_arrow (xp, old_stack_depth, this_depth);
|
||||
curr_frame
|
||||
= begin_html_stack_frame (xp,
|
||||
std::move (curr_frame),
|
||||
range->m_logical_loc,
|
||||
range->m_stack_depth,
|
||||
logical_loc_mgr);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (this_depth < curr_frame->m_stack_depth
|
||||
|| this_logical_loc != curr_frame->m_logical_loc)
|
||||
{
|
||||
curr_frame = end_html_stack_frame (xp, std::move (curr_frame));
|
||||
if (curr_frame == NULL)
|
||||
{
|
||||
curr_frame
|
||||
= begin_html_stack_frame (xp,
|
||||
nullptr,
|
||||
range->m_logical_loc,
|
||||
range->m_stack_depth,
|
||||
logical_loc_mgr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
emit_svg_arrow (xp, old_stack_depth, this_depth);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
curr_frame = begin_html_stack_frame (xp,
|
||||
NULL,
|
||||
range->m_logical_loc,
|
||||
range->m_stack_depth,
|
||||
logical_loc_mgr);
|
||||
}
|
||||
|
||||
xp.push_tag_with_class ("table", "event-range-with-margin", false);
|
||||
xp.push_tag ("tr", false);
|
||||
xp.push_tag_with_class ("td", "event-range", false);
|
||||
xp.push_tag_with_class ("div", "events-hdr", true);
|
||||
if (range->m_logical_loc)
|
||||
{
|
||||
gcc_assert (logical_loc_mgr);
|
||||
label_text funcname
|
||||
= logical_loc_mgr->get_name_for_path_output (range->m_logical_loc);
|
||||
if (funcname.get ())
|
||||
{
|
||||
xp.push_tag_with_class ("span", "funcname", true);
|
||||
xp.add_text (funcname.get ());
|
||||
xp.pop_tag (); //span
|
||||
xp.add_text (": ");
|
||||
}
|
||||
}
|
||||
{
|
||||
xp.push_tag_with_class ("span", "event-ids", true);
|
||||
pretty_printer pp;
|
||||
if (range->m_start_idx == range->m_end_idx)
|
||||
pp_printf (&pp, "event %i",
|
||||
range->m_start_idx + 1);
|
||||
else
|
||||
pp_printf (&pp, "events %i-%i",
|
||||
range->m_start_idx + 1, range->m_end_idx + 1);
|
||||
xp.add_text (pp_formatted_text (&pp));
|
||||
xp.pop_tag (); // span
|
||||
}
|
||||
if (show_depths)
|
||||
{
|
||||
xp.add_text (" ");
|
||||
xp.push_tag_with_class ("span", "depth", true);
|
||||
pretty_printer pp;
|
||||
pp_printf (&pp, "(depth %i)", range->m_stack_depth);
|
||||
xp.add_text (pp_formatted_text (&pp));
|
||||
xp.pop_tag (); //span
|
||||
}
|
||||
xp.pop_tag (); // div
|
||||
|
||||
/* Print a run of events. */
|
||||
thread_event_printer &tep = thread_event_printers[swimlane_idx];
|
||||
/* Wire up any trailing out-edge from previous range to leading in-edge
|
||||
of this range. */
|
||||
diagnostic_source_effect_info effect_info;
|
||||
effect_info.m_leading_in_edge_column = last_out_edge_column;
|
||||
tep.print_swimlane_for_event_range_as_html (dc, xp, event_label_writer,
|
||||
range, &effect_info);
|
||||
last_out_edge_column = effect_info.m_trailing_out_edge_column;
|
||||
|
||||
xp.pop_tag (); // td
|
||||
xp.pop_tag (); // tr
|
||||
xp.pop_tag (); // table
|
||||
}
|
||||
|
||||
/* Close outstanding frames. */
|
||||
while (curr_frame)
|
||||
curr_frame = end_html_stack_frame (xp, std::move (curr_frame));
|
||||
|
||||
xp.pop_tag (); // div
|
||||
}
|
||||
|
||||
} /* end of anonymous namespace for path-printing code. */
|
||||
|
||||
class element_event_desc : public pp_element
|
||||
@@ -1049,6 +1376,32 @@ diagnostic_text_output_format::print_path (const diagnostic_path &path)
|
||||
}
|
||||
}
|
||||
|
||||
/* Print PATH as HTML to XP, using DC and DSPP for settings.
|
||||
If non-null, use EVENT_LABEL_WRITER when writing events. */
|
||||
|
||||
void
|
||||
print_path_as_html (xml::printer &xp,
|
||||
const diagnostic_path &path,
|
||||
diagnostic_context &dc,
|
||||
html_label_writer *event_label_writer,
|
||||
const diagnostic_source_print_policy &dspp)
|
||||
{
|
||||
path_print_policy policy (dc);
|
||||
const bool check_rich_locations = true;
|
||||
const bool colorize = false;
|
||||
const diagnostic_source_printing_options &source_printing_opts
|
||||
= dspp.get_options ();
|
||||
const bool show_event_links = source_printing_opts.show_event_links_p;
|
||||
path_summary summary (policy,
|
||||
*dc.get_reference_printer (),
|
||||
path,
|
||||
check_rich_locations,
|
||||
colorize,
|
||||
show_event_links);
|
||||
print_path_summary_as_html (summary, dc, xp, event_label_writer,
|
||||
dc.show_path_depths_p ());
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -250,7 +250,10 @@ diagnostic_context::initialize (int n_opts)
|
||||
m_internal_error = nullptr;
|
||||
m_adjust_diagnostic_info = nullptr;
|
||||
m_text_callbacks.m_begin_diagnostic = default_diagnostic_text_starter;
|
||||
m_text_callbacks.m_start_span = default_diagnostic_start_span_fn;
|
||||
m_text_callbacks.m_text_start_span
|
||||
= default_diagnostic_start_span_fn<to_text>;
|
||||
m_text_callbacks.m_html_start_span
|
||||
= default_diagnostic_start_span_fn<to_html>;
|
||||
m_text_callbacks.m_end_diagnostic = default_diagnostic_text_finalizer;
|
||||
m_option_mgr = nullptr;
|
||||
m_urlifier_stack = new auto_vec<urlifier_stack_node> ();
|
||||
@@ -1071,21 +1074,6 @@ logical_location_manager::function_p (key k) const
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
default_diagnostic_start_span_fn (const diagnostic_location_print_policy &loc_policy,
|
||||
pretty_printer *pp,
|
||||
expanded_location exploc)
|
||||
{
|
||||
const diagnostic_column_policy &column_policy
|
||||
= loc_policy.get_column_policy ();
|
||||
label_text text
|
||||
= column_policy.get_location_text (exploc,
|
||||
loc_policy.show_column_p (),
|
||||
pp_show_color (pp));
|
||||
pp_string (pp, text.get ());
|
||||
pp_newline (pp);
|
||||
}
|
||||
|
||||
/* Interface to specify diagnostic kind overrides. Returns the
|
||||
previous setting, or DK_UNSPECIFIED if the parameters are out of
|
||||
range. If OPTION_ID is zero, the new setting is for all the
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace text_art
|
||||
class theme;
|
||||
} // namespace text_art
|
||||
|
||||
namespace xml
|
||||
{
|
||||
class printer;
|
||||
} // namespace xml
|
||||
|
||||
/* An enum for controlling what units to use for the column number
|
||||
when diagnostics are output, used by the -fdiagnostics-column-unit option.
|
||||
Tabs will be expanded or not according to the value of -ftabstop. The origin
|
||||
@@ -177,10 +182,15 @@ class diagnostic_source_print_policy;
|
||||
typedef void (*diagnostic_text_starter_fn) (diagnostic_text_output_format &,
|
||||
const diagnostic_info *);
|
||||
|
||||
typedef void
|
||||
(*diagnostic_start_span_fn) (const diagnostic_location_print_policy &,
|
||||
pretty_printer *,
|
||||
expanded_location);
|
||||
struct to_text;
|
||||
struct to_html;
|
||||
|
||||
extern pretty_printer *get_printer (to_text &);
|
||||
|
||||
template <typename Sink>
|
||||
using diagnostic_start_span_fn = void (*) (const diagnostic_location_print_policy &,
|
||||
Sink &sink,
|
||||
expanded_location);
|
||||
|
||||
typedef void (*diagnostic_text_finalizer_fn) (diagnostic_text_output_format &,
|
||||
const diagnostic_info *,
|
||||
@@ -389,11 +399,32 @@ public:
|
||||
const diagnostic_column_policy &
|
||||
get_column_policy () const { return m_column_policy; }
|
||||
|
||||
void
|
||||
print_text_span_start (const diagnostic_context &dc,
|
||||
pretty_printer &pp,
|
||||
const expanded_location &exploc);
|
||||
|
||||
void
|
||||
print_html_span_start (const diagnostic_context &dc,
|
||||
xml::printer &xp,
|
||||
const expanded_location &exploc);
|
||||
|
||||
private:
|
||||
diagnostic_column_policy m_column_policy;
|
||||
bool m_show_column;
|
||||
};
|
||||
|
||||
/* Abstract base class for optionally supplying extra tags when writing
|
||||
out annotation labels in HTML output. */
|
||||
|
||||
class html_label_writer
|
||||
{
|
||||
public:
|
||||
virtual ~html_label_writer () {}
|
||||
virtual void begin_label () = 0;
|
||||
virtual void end_label () = 0;
|
||||
};
|
||||
|
||||
/* A bundle of state for printing source within a diagnostic,
|
||||
to isolate the interactions between diagnostic_context and the
|
||||
implementation of diagnostic_show_locus. */
|
||||
@@ -411,11 +442,21 @@ public:
|
||||
diagnostic_t diagnostic_kind,
|
||||
diagnostic_source_effect_info *effect_info) const;
|
||||
|
||||
void
|
||||
print_as_html (xml::printer &xp,
|
||||
const rich_location &richloc,
|
||||
diagnostic_t diagnostic_kind,
|
||||
diagnostic_source_effect_info *effect_info,
|
||||
html_label_writer *label_writer) const;
|
||||
|
||||
const diagnostic_source_printing_options &
|
||||
get_options () const { return m_options; }
|
||||
|
||||
diagnostic_start_span_fn
|
||||
get_start_span_fn () const { return m_start_span_cb; }
|
||||
diagnostic_start_span_fn<to_text>
|
||||
get_text_start_span_fn () const { return m_text_start_span_cb; }
|
||||
|
||||
diagnostic_start_span_fn<to_html>
|
||||
get_html_start_span_fn () const { return m_html_start_span_cb; }
|
||||
|
||||
file_cache &
|
||||
get_file_cache () const { return m_file_cache; }
|
||||
@@ -442,7 +483,8 @@ public:
|
||||
private:
|
||||
const diagnostic_source_printing_options &m_options;
|
||||
class diagnostic_location_print_policy m_location_policy;
|
||||
diagnostic_start_span_fn m_start_span_cb;
|
||||
diagnostic_start_span_fn<to_text> m_text_start_span_cb;
|
||||
diagnostic_start_span_fn<to_html> m_html_start_span_cb;
|
||||
file_cache &m_file_cache;
|
||||
|
||||
/* Other data copied from diagnostic_context. */
|
||||
@@ -508,7 +550,7 @@ public:
|
||||
/* Give access to m_text_callbacks. */
|
||||
friend diagnostic_text_starter_fn &
|
||||
diagnostic_text_starter (diagnostic_context *context);
|
||||
friend diagnostic_start_span_fn &
|
||||
friend diagnostic_start_span_fn<to_text> &
|
||||
diagnostic_start_span (diagnostic_context *context);
|
||||
friend diagnostic_text_finalizer_fn &
|
||||
diagnostic_text_finalizer (diagnostic_context *context);
|
||||
@@ -602,6 +644,12 @@ public:
|
||||
diagnostic_t diagnostic_kind,
|
||||
pretty_printer &pp,
|
||||
diagnostic_source_effect_info *effect_info);
|
||||
void maybe_show_locus_as_html (const rich_location &richloc,
|
||||
const diagnostic_source_printing_options &opts,
|
||||
diagnostic_t diagnostic_kind,
|
||||
xml::printer &xp,
|
||||
diagnostic_source_effect_info *effect_info,
|
||||
html_label_writer *label_writer);
|
||||
|
||||
void emit_diagram (const diagnostic_diagram &diagram);
|
||||
|
||||
@@ -882,7 +930,8 @@ private:
|
||||
/* This function is called by diagnostic_show_locus in between
|
||||
disjoint spans of source code, so that the context can print
|
||||
something to indicate that a new span of source code has begun. */
|
||||
diagnostic_start_span_fn m_start_span;
|
||||
diagnostic_start_span_fn<to_text> m_text_start_span;
|
||||
diagnostic_start_span_fn<to_html> m_html_start_span;
|
||||
|
||||
/* This function is called after the diagnostic message is printed. */
|
||||
diagnostic_text_finalizer_fn m_end_diagnostic;
|
||||
@@ -1040,10 +1089,10 @@ diagnostic_text_starter (diagnostic_context *context)
|
||||
/* Client supplied function called between disjoint spans of source code,
|
||||
so that the context can print
|
||||
something to indicate that a new span of source code has begun. */
|
||||
inline diagnostic_start_span_fn &
|
||||
inline diagnostic_start_span_fn<to_text> &
|
||||
diagnostic_start_span (diagnostic_context *context)
|
||||
{
|
||||
return context->m_text_callbacks.m_start_span;
|
||||
return context->m_text_callbacks.m_text_start_span;
|
||||
}
|
||||
|
||||
/* Client supplied function called after a diagnostic message is
|
||||
@@ -1128,6 +1177,21 @@ diagnostic_show_locus (diagnostic_context *context,
|
||||
context->maybe_show_locus (*richloc, opts, diagnostic_kind, *pp, effect_info);
|
||||
}
|
||||
|
||||
inline void
|
||||
diagnostic_show_locus_as_html (diagnostic_context *context,
|
||||
const diagnostic_source_printing_options &opts,
|
||||
rich_location *richloc,
|
||||
diagnostic_t diagnostic_kind,
|
||||
xml::printer &xp,
|
||||
diagnostic_source_effect_info *effect_info = nullptr,
|
||||
html_label_writer *label_writer = nullptr)
|
||||
{
|
||||
gcc_assert (context);
|
||||
gcc_assert (richloc);
|
||||
context->maybe_show_locus_as_html (*richloc, opts, diagnostic_kind, xp,
|
||||
effect_info, label_writer);
|
||||
}
|
||||
|
||||
/* Because we read source files a second time after the frontend did it the
|
||||
first time, we need to know how the frontend handled things like character
|
||||
set conversion and UTF-8 BOM stripping, in order to make everything
|
||||
@@ -1201,8 +1265,9 @@ extern void diagnostic_set_info_translated (diagnostic_info *, const char *,
|
||||
#endif
|
||||
void default_diagnostic_text_starter (diagnostic_text_output_format &,
|
||||
const diagnostic_info *);
|
||||
template <typename Sink>
|
||||
void default_diagnostic_start_span_fn (const diagnostic_location_print_policy &,
|
||||
pretty_printer *,
|
||||
Sink &sink,
|
||||
expanded_location);
|
||||
void default_diagnostic_text_finalizer (diagnostic_text_output_format &,
|
||||
const diagnostic_info *,
|
||||
|
||||
@@ -6119,18 +6119,25 @@ in this release.
|
||||
|
||||
@item experimental-html
|
||||
Emit diagnostics to a file in HTML format. This scheme is experimental,
|
||||
and may go away in future GCC releases. The details of the output are
|
||||
also subject to change.
|
||||
and may go away in future GCC releases. The keys and details of the output
|
||||
are also subject to change.
|
||||
|
||||
Supported keys are:
|
||||
|
||||
@table @gcctabopt
|
||||
|
||||
@item css=@r{[}yes@r{|}no@r{]}
|
||||
Add an embedded <style> to the generated HTML. Defaults to yes.
|
||||
|
||||
@item file=@var{FILENAME}
|
||||
Specify the filename to write the HTML output to, potentially with a
|
||||
leading absolute or relative path. If not specified, it defaults to
|
||||
@file{@var{source}.html}.
|
||||
|
||||
@item javascript=@r{[}yes@r{|}no@r{]}
|
||||
Add an embedded <script> to the generated HTML providing a barebones UI
|
||||
for viewing results. Defaults to yes.
|
||||
|
||||
@end table
|
||||
|
||||
@end table
|
||||
|
||||
@@ -618,9 +618,10 @@ gfc_diagnostic_text_starter (diagnostic_text_output_format &text_output,
|
||||
|
||||
static void
|
||||
gfc_diagnostic_start_span (const diagnostic_location_print_policy &loc_policy,
|
||||
pretty_printer *pp,
|
||||
to_text &sink,
|
||||
expanded_location exploc)
|
||||
{
|
||||
pretty_printer *pp = get_printer (sink);
|
||||
const bool colorize = pp_show_color (pp);
|
||||
char *locus_prefix
|
||||
= gfc_diagnostic_build_locus_prefix (loc_policy, exploc, colorize);
|
||||
|
||||
@@ -545,21 +545,40 @@ html_scheme_handler::make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const scheme_name_and_params &parsed_arg) const
|
||||
{
|
||||
bool css = true;
|
||||
label_text filename;
|
||||
bool javascript = true;
|
||||
for (auto& iter : parsed_arg.m_kvs)
|
||||
{
|
||||
const std::string &key = iter.first;
|
||||
const std::string &value = iter.second;
|
||||
if (key == "css")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
css))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "file")
|
||||
{
|
||||
filename = label_text::take (xstrdup (value.c_str ()));
|
||||
continue;
|
||||
}
|
||||
if (key == "javascript")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value,
|
||||
javascript))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Key not found. */
|
||||
auto_vec<const char *> known_keys;
|
||||
known_keys.safe_push ("css");
|
||||
known_keys.safe_push ("file");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (), known_keys);
|
||||
known_keys.safe_push ("javascript");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
|
||||
known_keys);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -579,8 +598,13 @@ html_scheme_handler::make_sink (const context &ctxt,
|
||||
if (!output_file)
|
||||
return nullptr;
|
||||
|
||||
html_generation_options html_gen_opts;
|
||||
html_gen_opts.m_css = css;
|
||||
html_gen_opts.m_javascript = javascript;
|
||||
|
||||
auto sink = make_html_sink (ctxt.m_dc,
|
||||
*line_table,
|
||||
html_gen_opts,
|
||||
std::move (output_file));
|
||||
return sink;
|
||||
}
|
||||
|
||||
@@ -58,11 +58,11 @@ test_diagnostic_context::~test_diagnostic_context ()
|
||||
void
|
||||
test_diagnostic_context::
|
||||
start_span_cb (const diagnostic_location_print_policy &loc_policy,
|
||||
pretty_printer *pp,
|
||||
to_text &sink,
|
||||
expanded_location exploc)
|
||||
{
|
||||
exploc.file = "FILENAME";
|
||||
default_diagnostic_start_span_fn (loc_policy, pp, exploc);
|
||||
default_diagnostic_start_span_fn<to_text> (loc_policy, sink, exploc);
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -40,7 +40,7 @@ class test_diagnostic_context : public diagnostic_context
|
||||
real filename (to avoid printing the names of tempfiles). */
|
||||
static void
|
||||
start_span_cb (const diagnostic_location_print_policy &,
|
||||
pretty_printer *,
|
||||
to_text &sink,
|
||||
expanded_location exploc);
|
||||
|
||||
/* Report a diagnostic to this context. For a selftest, this
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-fdiagnostics-add-output=experimental-html" } */
|
||||
/* { dg-options "-fdiagnostics-add-output=experimental-html:javascript=no" } */
|
||||
|
||||
/* Verify that basics of HTML output work. */
|
||||
|
||||
|
||||
@@ -18,12 +18,6 @@ import pytest
|
||||
def html_tree():
|
||||
return html_tree_from_env()
|
||||
|
||||
XHTML = 'http://www.w3.org/1999/xhtml'
|
||||
ns = {'xhtml': XHTML}
|
||||
|
||||
def make_tag(local_name):
|
||||
return f'{{{XHTML}}}' + local_name
|
||||
|
||||
def test_basics(html_tree):
|
||||
root = html_tree.getroot ()
|
||||
assert root.tag == make_tag('html')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-fdiagnostics-set-output=experimental-html" } */
|
||||
/* { dg-additional-options "-fdiagnostics-show-caret" } */
|
||||
/* { dg-options "-fdiagnostics-set-output=experimental-html:javascript=no" } */
|
||||
/* { dg-additional-options "-fdiagnostics-show-caret -fdiagnostics-show-line-numbers" } */
|
||||
|
||||
extern char *gets (char *s);
|
||||
|
||||
|
||||
@@ -8,12 +8,6 @@ import pytest
|
||||
def html_tree():
|
||||
return html_tree_from_env()
|
||||
|
||||
XHTML = 'http://www.w3.org/1999/xhtml'
|
||||
ns = {'xhtml': XHTML}
|
||||
|
||||
def make_tag(local_name):
|
||||
return f'{{{XHTML}}}' + local_name
|
||||
|
||||
def test_metadata(html_tree):
|
||||
root = html_tree.getroot ()
|
||||
assert root.tag == make_tag('html')
|
||||
@@ -48,11 +42,21 @@ def test_metadata(html_tree):
|
||||
assert metadata[1][0].text == 'STR34-C'
|
||||
assert metadata[1][0].tail == ']'
|
||||
|
||||
src = diag.find('xhtml:pre', ns)
|
||||
assert src.attrib['class'] == 'gcc-annotated-source'
|
||||
assert src.text == (
|
||||
' gets (buf);\n'
|
||||
' ^~~~~~~~~~\n')
|
||||
src = diag.find('xhtml:table', ns)
|
||||
assert src.attrib['class'] == 'locus'
|
||||
|
||||
tbody = src.find('xhtml:tbody', ns)
|
||||
assert tbody.attrib['class'] == 'line-span'
|
||||
|
||||
rows = tbody.findall('xhtml:tr', ns)
|
||||
|
||||
quoted_src_tr = rows[0]
|
||||
assert_quoted_line(quoted_src_tr,
|
||||
' 10', ' gets (buf);')
|
||||
|
||||
annotation_tr = rows[1]
|
||||
assert_annotation_line(annotation_tr,
|
||||
' ^~~~~~~~~~')
|
||||
|
||||
# For reference, here's the generated HTML:
|
||||
"""
|
||||
@@ -60,8 +64,13 @@ def test_metadata(html_tree):
|
||||
<div class="gcc-diagnostic-list">
|
||||
<div class="gcc-diagnostic">
|
||||
<span class="gcc-message">never use '<span class="gcc-quoted-text">gets</span>'</span>
|
||||
<span class="gcc-metadata"><span class="gcc-metadata-item">[<a href="https://cwe.mitre.org/data/definitions/242.html">CWE-242</a>]</span><span class="gcc-metadata-item">[<a href="https://example.com/">STR34-C</a>]</span></span>
|
||||
...etc...
|
||||
<span class="gcc-metadata"><span class="gcc-metadata-item">[<a href="https://cwe.mitre.org/data/definitions/242.html">CWE-242</a>]</span><span class="gcc-metadata-item">[<a href="https://example.com/">STR34-C</a>]</span></span><table class="locus">
|
||||
<tbody class="line-span">
|
||||
<tr><td class="linenum"> 10</td> <td class="source"> gets (buf);</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> ^~~~~~~~~~</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-fdiagnostics-show-caret -fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-add-output=experimental-html" } */
|
||||
/* { dg-options "-fdiagnostics-show-caret -fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-add-output=experimental-html:javascript=no" } */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -8,12 +8,6 @@ import pytest
|
||||
def html_tree():
|
||||
return html_tree_from_env()
|
||||
|
||||
XHTML = 'http://www.w3.org/1999/xhtml'
|
||||
ns = {'xhtml': XHTML}
|
||||
|
||||
def make_tag(local_name):
|
||||
return f'{{{XHTML}}}' + local_name
|
||||
|
||||
def test_paths(html_tree):
|
||||
root = html_tree.getroot ()
|
||||
assert root.tag == make_tag('html')
|
||||
@@ -29,7 +23,19 @@ def test_paths(html_tree):
|
||||
assert diag is not None
|
||||
assert diag.attrib['class'] == 'gcc-diagnostic'
|
||||
|
||||
pre = diag.findall('xhtml:pre', ns)
|
||||
assert pre[0].attrib['class'] == 'gcc-annotated-source'
|
||||
assert pre[1].attrib['class'] == 'gcc-execution-path'
|
||||
assert pre[1].text.startswith(" 'make_a_list_of_random_ints_badly': events 1-3")
|
||||
event_ranges = diag.find('xhtml:div', ns)
|
||||
assert_class(event_ranges, 'event-ranges')
|
||||
|
||||
frame_margin = event_ranges.find('xhtml:table', ns)
|
||||
assert_class(frame_margin, 'stack-frame-with-margin')
|
||||
|
||||
tr = frame_margin.find('xhtml:tr', ns)
|
||||
assert tr is not None
|
||||
tds = tr.findall('xhtml:td', ns)
|
||||
assert len(tds) == 2
|
||||
|
||||
assert_class(tds[0], 'interprocmargin')
|
||||
|
||||
test_frame = tds[1]
|
||||
assert_frame(test_frame, 'make_a_list_of_random_ints_badly')
|
||||
assert_event_range_with_margin(test_frame[1])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fdiagnostics-show-line-numbers" } */
|
||||
/* { dg-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fdiagnostics-show-line-numbers -fdiagnostics-add-output=experimental-html:javascript=no" } */
|
||||
/* { dg-enable-nn-line-numbers "" } */
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -82,3 +82,7 @@ void test (void)
|
||||
| | (9) calling 'fprintf'
|
||||
|
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
HTML file:
|
||||
{ dg-final { run-html-pytest diagnostic-test-paths-4.c "diagnostic-test-paths-4.py" } } */
|
||||
|
||||
190
gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-4.py
Normal file
190
gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-4.py
Normal file
@@ -0,0 +1,190 @@
|
||||
# Verify that interprocedural execution paths work in HTML output.
|
||||
|
||||
from htmltest import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def html_tree():
|
||||
return html_tree_from_env()
|
||||
|
||||
def test_paths(html_tree):
|
||||
diag = get_diag_by_index(html_tree, 0)
|
||||
src = get_locus_within_diag (diag)
|
||||
|
||||
tbody = src.find('xhtml:tbody', ns)
|
||||
assert_class(tbody, 'line-span')
|
||||
|
||||
rows = tbody.findall('xhtml:tr', ns)
|
||||
|
||||
quoted_src_tr = rows[0]
|
||||
assert_quoted_line(quoted_src_tr,
|
||||
' 13', ' fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to \'fprintf\' from within signal handler" } */')
|
||||
|
||||
annotation_tr = rows[1]
|
||||
assert_annotation_line(annotation_tr,
|
||||
' ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
|
||||
|
||||
event_ranges = diag.find('xhtml:div', ns)
|
||||
assert_class(event_ranges, 'event-ranges')
|
||||
|
||||
test_frame_margin = event_ranges.find('xhtml:table', ns)
|
||||
assert_class(test_frame_margin, 'stack-frame-with-margin')
|
||||
|
||||
tr = test_frame_margin.find('xhtml:tr', ns)
|
||||
assert tr is not None
|
||||
tds = tr.findall('xhtml:td', ns)
|
||||
assert len(tds) == 2
|
||||
|
||||
assert_class(tds[0], 'interprocmargin')
|
||||
|
||||
test_frame = tds[1]
|
||||
assert_frame(test_frame, 'test')
|
||||
assert_event_range_with_margin(test_frame[1])
|
||||
|
||||
# For reference, generated HTML looks like this:
|
||||
"""
|
||||
<table class="stack-frame-with-margin"><tr>
|
||||
<td class="interprocmargin" style="padding-left: 100px"/>
|
||||
<td class="stack-frame">
|
||||
<div class="frame-funcname"><span>test</span></div><table class="event-range-with-margin"><tr>
|
||||
<td class="event-range">
|
||||
<div class="events-hdr"><span class="funcname">test</span>: <span class="event-ids">events 1-2</span></div>
|
||||
<table class="locus">
|
||||
<tbody class="line-span">
|
||||
<tr><td class="linenum"> 27</td> <td class="source">{</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">^</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">|</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">(1) entering 'test'</td></tr>
|
||||
<tr><td class="linenum"> 28</td> <td class="source"> register_handler ();</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> ~~~~~~~~~~~~~~~~~~~</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> |</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> (2) calling 'register_handler'</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td></tr></table>
|
||||
<div class="between-ranges-call">
|
||||
<svg height="30" width="150">
|
||||
<defs>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7"
|
||||
refX="0" refY="3.5" orient="auto" stroke="#0088ce" fill="#0088ce">
|
||||
<polygon points="0 0, 10 3.5, 0 7"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<polyline points="20,0 20,10 120,10 120,20"
|
||||
style="fill:none;stroke: #0088ce"
|
||||
marker-end="url(#arrowhead)"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<table class="stack-frame-with-margin"><tr>
|
||||
<td class="interprocmargin" style="padding-left: 100px"/>
|
||||
<td class="stack-frame">
|
||||
<div class="frame-funcname"><span>register_handler</span></div><table class="event-range-with-margin"><tr>
|
||||
<td class="event-range">
|
||||
<div class="events-hdr"><span class="funcname">register_handler</span>: <span class="event-ids">events 3-4</span></div>
|
||||
<table class="locus">
|
||||
<tbody class="line-span">
|
||||
<tr><td class="linenum"> 22</td> <td class="source">{</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">^</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">|</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">(3) entering 'register_handler'</td></tr>
|
||||
<tr><td class="linenum"> 23</td> <td class="source"> signal(SIGINT, int_handler);</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> ~~~~~~~~~~~~~~~~~~~~~~~~~~~</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> |</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> (4) registering 'int_handler' as signal handler</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td></tr></table>
|
||||
</td></tr></table>
|
||||
</td></tr></table>
|
||||
<div class="between-ranges-return">
|
||||
<svg height="30" width="250">
|
||||
<defs>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7"
|
||||
refX="0" refY="3.5" orient="auto" stroke="#0088ce" fill="#0088ce">
|
||||
<polygon points="0 0, 10 3.5, 0 7"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<polyline points="220,0 220,10 20,10 20,20"
|
||||
style="fill:none;stroke: #0088ce"
|
||||
marker-end="url(#arrowhead)"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<table class="event-range-with-margin"><tr>
|
||||
<td class="event-range">
|
||||
<div class="events-hdr"><span class="event-ids">event 5</span></div>
|
||||
(5): later on, when the signal is delivered to the process
|
||||
</td></tr></table>
|
||||
<div class="between-ranges-call">
|
||||
<svg height="30" width="150">
|
||||
<defs>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7"
|
||||
refX="0" refY="3.5" orient="auto" stroke="#0088ce" fill="#0088ce">
|
||||
<polygon points="0 0, 10 3.5, 0 7"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<polyline points="20,0 20,10 120,10 120,20"
|
||||
style="fill:none;stroke: #0088ce"
|
||||
marker-end="url(#arrowhead)"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<table class="stack-frame-with-margin"><tr>
|
||||
<td class="interprocmargin" style="padding-left: 100px"/>
|
||||
<td class="stack-frame">
|
||||
<div class="frame-funcname"><span>int_handler</span></div><table class="event-range-with-margin"><tr>
|
||||
<td class="event-range">
|
||||
<div class="events-hdr"><span class="funcname">int_handler</span>: <span class="event-ids">events 6-7</span></div>
|
||||
<table class="locus">
|
||||
<tbody class="line-span">
|
||||
<tr><td class="linenum"> 17</td> <td class="source">{</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">^</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">|</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">(6) entering 'int_handler'</td></tr>
|
||||
<tr><td class="linenum"> 18</td> <td class="source"> custom_logger("got signal");</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> ~~~~~~~~~~~~~~~~~~~~~~~~~~~</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> |</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> (7) calling 'custom_logger'</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td></tr></table>
|
||||
<div class="between-ranges-call">
|
||||
<svg height="30" width="150">
|
||||
<defs>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7"
|
||||
refX="0" refY="3.5" orient="auto" stroke="#0088ce" fill="#0088ce">
|
||||
<polygon points="0 0, 10 3.5, 0 7"/>
|
||||
</marker>
|
||||
</defs>
|
||||
<polyline points="20,0 20,10 120,10 120,20"
|
||||
style="fill:none;stroke: #0088ce"
|
||||
marker-end="url(#arrowhead)"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<table class="stack-frame-with-margin"><tr>
|
||||
<td class="interprocmargin" style="padding-left: 100px"/>
|
||||
<td class="stack-frame">
|
||||
<div class="frame-funcname"><span>custom_logger</span></div><table class="event-range-with-margin"><tr>
|
||||
<td class="event-range">
|
||||
<div class="events-hdr"><span class="funcname">custom_logger</span>: <span class="event-ids">events 8-9</span></div>
|
||||
<table class="locus">
|
||||
<tbody class="line-span">
|
||||
<tr><td class="linenum"> 12</td> <td class="source">{</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">^</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">|</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation">(8) entering 'custom_logger'</td></tr>
|
||||
<tr><td class="linenum"> 13</td> <td class="source"> fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> |</td></tr>
|
||||
<tr><td class="linenum"/><td class="annotation"> (9) calling 'fprintf'</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td></tr></table>
|
||||
</td></tr></table>
|
||||
</td></tr></table>
|
||||
|
||||
</div>
|
||||
"""
|
||||
@@ -1,5 +1,5 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O -fdiagnostics-show-caret -fdiagnostics-show-line-numbers" } */
|
||||
/* { dg-options "-O -fdiagnostics-show-caret -fdiagnostics-show-line-numbers -fdiagnostics-add-output=experimental-html:javascript=no" } */
|
||||
|
||||
/* This is a collection of unittests for diagnostic_show_locus;
|
||||
see the overview in diagnostic_plugin_test_show_locus.c.
|
||||
@@ -118,3 +118,7 @@ void test_fixit_insert_newline (void)
|
||||
{ dg-end-multiline-output "" } */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
HTML file:
|
||||
{ dg-final { run-html-pytest diagnostic-test-show-locus-bw-line-numbers.c "diagnostic-test-show-locus.py" } } */
|
||||
|
||||
111
gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus.py
Normal file
111
gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus.py
Normal file
@@ -0,0 +1,111 @@
|
||||
# Verify that diagnostic-show-locus.cc works with HTML output.
|
||||
|
||||
from htmltest import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def html_tree():
|
||||
return html_tree_from_env()
|
||||
|
||||
#def get_tr_within_thead(thead, idx)
|
||||
|
||||
def get_ruler_text(thead, idx):
|
||||
trs = thead.findall('xhtml:tr', ns)
|
||||
tr = trs[idx]
|
||||
tds = tr.findall('xhtml:td', ns)
|
||||
assert len(tds) == 3
|
||||
assert_class(tds[2], 'ruler')
|
||||
return tds[2].text
|
||||
|
||||
def test_very_wide_line(html_tree):
|
||||
diag = get_diag_by_index(html_tree, 2)
|
||||
src = get_locus_within_diag(diag)
|
||||
|
||||
# Check ruler
|
||||
thead = src.find('xhtml:thead', ns)
|
||||
assert_class(thead, 'ruler')
|
||||
trs = thead.findall('xhtml:tr', ns)
|
||||
assert len(trs) == 3
|
||||
|
||||
assert get_ruler_text(thead, 0) == ' 0 0 0 0 0 1 1 '
|
||||
assert get_ruler_text(thead, 1) == ' 5 6 7 8 9 0 1 '
|
||||
assert get_ruler_text(thead, 2) == '34567890123456789012345678901234567890123456789012345678901234567890123'
|
||||
|
||||
# Check quoted source
|
||||
tbody = src.find('xhtml:tbody', ns)
|
||||
assert_class(tbody, 'line-span')
|
||||
trs = tbody.findall('xhtml:tr', ns)
|
||||
assert len(trs) == 5
|
||||
assert_quoted_line(trs[0], ' 43', ' float f = foo * bar; /* { dg-warning "95: test" } */')
|
||||
assert_annotation_line(trs[1], ' ~~~~^~~~~')
|
||||
assert_annotation_line(trs[2], ' |')
|
||||
assert_annotation_line(trs[3], ' label 0')
|
||||
assert_annotation_line(trs[4], ' bar * foo')
|
||||
|
||||
def test_fixit_insert(html_tree):
|
||||
diag = get_diag_by_index(html_tree, 3)
|
||||
msg = get_message_within_diag(diag)
|
||||
assert msg.text == 'example of insertion hints'
|
||||
|
||||
src = get_locus_within_diag(diag)
|
||||
|
||||
# Check quoted source
|
||||
tbody = src.find('xhtml:tbody', ns)
|
||||
assert_class(tbody, 'line-span')
|
||||
trs = tbody.findall('xhtml:tr', ns)
|
||||
assert len(trs) == 3
|
||||
assert_quoted_line(trs[0], ' 63', ' int a[2][2] = { 0, 1 , 2, 3 }; /* { dg-warning "insertion hints" } */')
|
||||
assert_annotation_line(trs[1], ' ^~~~')
|
||||
assert_annotation_line(trs[2], ' { }')
|
||||
|
||||
def test_fixit_remove(html_tree):
|
||||
diag = get_diag_by_index(html_tree, 4)
|
||||
msg = get_message_within_diag(diag)
|
||||
assert msg.text == 'example of a removal hint'
|
||||
|
||||
src = get_locus_within_diag(diag)
|
||||
|
||||
# Check quoted source
|
||||
tbody = src.find('xhtml:tbody', ns)
|
||||
assert_class(tbody, 'line-span')
|
||||
trs = tbody.findall('xhtml:tr', ns)
|
||||
assert len(trs) == 3
|
||||
assert_quoted_line(trs[0], ' 77', ' int a;; /* { dg-warning "example of a removal hint" } */')
|
||||
assert_annotation_line(trs[1], ' ^')
|
||||
assert_annotation_line(trs[2], ' -')
|
||||
|
||||
def test_fixit_replace(html_tree):
|
||||
diag = get_diag_by_index(html_tree, 5)
|
||||
msg = get_message_within_diag(diag)
|
||||
assert msg.text == 'example of a replacement hint'
|
||||
|
||||
src = get_locus_within_diag(diag)
|
||||
|
||||
# Check quoted source
|
||||
tbody = src.find('xhtml:tbody', ns)
|
||||
assert_class(tbody, 'line-span')
|
||||
trs = tbody.findall('xhtml:tr', ns)
|
||||
assert len(trs) == 3
|
||||
assert_quoted_line(trs[0], ' 91', ' gtk_widget_showall (dlg); /* { dg-warning "example of a replacement hint" } */')
|
||||
assert_annotation_line(trs[1], ' ^~~~~~~~~~~~~~~~~~')
|
||||
assert_annotation_line(trs[2], ' gtk_widget_show_all')
|
||||
|
||||
def test_fixit_insert_newline(html_tree):
|
||||
diag = get_diag_by_index(html_tree, 6)
|
||||
msg = get_message_within_diag(diag)
|
||||
assert msg.text == 'example of newline insertion hint'
|
||||
|
||||
src = get_locus_within_diag(diag)
|
||||
|
||||
# Check quoted source
|
||||
tbody = src.find('xhtml:tbody', ns)
|
||||
assert_class(tbody, 'line-span')
|
||||
trs = tbody.findall('xhtml:tr', ns)
|
||||
assert len(trs) == 4
|
||||
assert_quoted_line(trs[0], ' 109', ' x = a;')
|
||||
assert_annotation_line(trs[1], ' break;',
|
||||
expected_line_num=' +++',
|
||||
expected_left_margin='+')
|
||||
assert_quoted_line(trs[2], ' 110', " case 'b': /* { dg-warning \"newline insertion\" } */")
|
||||
assert_annotation_line(trs[3], ' ^~~~~~~~')
|
||||
@@ -176,9 +176,10 @@ test_diagnostic_text_starter (diagnostic_text_output_format &text_output,
|
||||
|
||||
void
|
||||
test_diagnostic_start_span_fn (const diagnostic_location_print_policy &,
|
||||
pretty_printer *pp,
|
||||
to_text &sink,
|
||||
expanded_location)
|
||||
{
|
||||
pretty_printer *pp = get_printer (sink);
|
||||
pp_string (pp, "START_SPAN_FN: ");
|
||||
pp_newline (pp);
|
||||
}
|
||||
|
||||
@@ -7,3 +7,93 @@ def html_tree_from_env():
|
||||
html_filename += '.html'
|
||||
print('html_filename: %r' % html_filename)
|
||||
return ET.parse(html_filename)
|
||||
|
||||
XHTML = 'http://www.w3.org/1999/xhtml'
|
||||
ns = {'xhtml': XHTML}
|
||||
|
||||
def make_tag(local_name):
|
||||
return f'{{{XHTML}}}' + local_name
|
||||
|
||||
def assert_tag(element, expected):
|
||||
assert element.tag == make_tag(expected)
|
||||
|
||||
def assert_class(element, expected):
|
||||
assert element.attrib['class'] == expected
|
||||
|
||||
def assert_quoted_line(tr, expected_line_num, expected_src):
|
||||
"""Verify that tr is a line of quoted source."""
|
||||
tds = tr.findall('xhtml:td', ns)
|
||||
assert len(tds) == 3
|
||||
assert_class(tds[0], 'linenum')
|
||||
assert tds[0].text == expected_line_num
|
||||
assert_class(tds[1], 'left-margin')
|
||||
assert tds[1].text == ' '
|
||||
assert_class(tds[2], 'source')
|
||||
assert tds[2].text == expected_src
|
||||
|
||||
def assert_annotation_line(tr, expected_src,
|
||||
expected_line_num=' ',
|
||||
expected_left_margin=' '):
|
||||
"""Verify that tr is an annotation line."""
|
||||
tds = tr.findall('xhtml:td', ns)
|
||||
assert len(tds) == 3
|
||||
assert_class(tds[0], 'linenum')
|
||||
assert tds[0].text == expected_line_num
|
||||
assert_class(tds[1], 'left-margin')
|
||||
assert tds[1].text == expected_left_margin
|
||||
assert_class(tds[2], 'annotation')
|
||||
assert tds[2].text == expected_src
|
||||
|
||||
def assert_frame(frame, expected_fnname):
|
||||
"""
|
||||
Assert that frame is of class 'stack-frame'
|
||||
and has a child showing the expected fnname.
|
||||
"""
|
||||
assert_class(frame, 'stack-frame')
|
||||
funcname = frame[0]
|
||||
assert_class(funcname, 'frame-funcname')
|
||||
span = funcname[0]
|
||||
assert_tag(span, 'span')
|
||||
assert span.text == expected_fnname
|
||||
|
||||
def assert_event_range_with_margin(element):
|
||||
"""
|
||||
Verify that "element" is an event-range-with-margin
|
||||
"""
|
||||
assert_tag(element, 'table')
|
||||
assert_class(element, 'event-range-with-margin')
|
||||
tr = element.find('xhtml:tr', ns)
|
||||
assert tr is not None
|
||||
td = tr.find('xhtml:td', ns)
|
||||
assert_class(td, 'event-range')
|
||||
|
||||
events_hdr = td.find('xhtml:div', ns)
|
||||
assert_class(events_hdr, 'events-hdr')
|
||||
|
||||
#...etc
|
||||
|
||||
def get_diag_by_index(html_tree, index):
|
||||
root = html_tree.getroot ()
|
||||
assert root.tag == make_tag('html')
|
||||
|
||||
body = root.find('xhtml:body', ns)
|
||||
assert body is not None
|
||||
|
||||
diag_list = body.find('xhtml:div', ns)
|
||||
assert diag_list is not None
|
||||
assert_class(diag_list, 'gcc-diagnostic-list')
|
||||
|
||||
diags = diag_list.findall('xhtml:div', ns)
|
||||
diag = diags[index]
|
||||
assert_class(diag, 'gcc-diagnostic')
|
||||
return diag
|
||||
|
||||
def get_message_within_diag(diag_element):
|
||||
msg = diag_element.find('xhtml:span', ns)
|
||||
assert_class(msg, 'gcc-message')
|
||||
return msg
|
||||
|
||||
def get_locus_within_diag(diag_element):
|
||||
src = diag_element.find('xhtml:table', ns)
|
||||
assert_class(src, 'locus')
|
||||
return src
|
||||
|
||||
84
gcc/xml-printer.h
Normal file
84
gcc/xml-printer.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/* Classes for creating XML trees by appending.
|
||||
Copyright (C) 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/>. */
|
||||
|
||||
#ifndef GCC_XML_PRINTER_H
|
||||
#define GCC_XML_PRINTER_H
|
||||
|
||||
namespace xml {
|
||||
|
||||
class node;
|
||||
class element;
|
||||
|
||||
/* A class for creating XML trees by appending to an insertion
|
||||
point, with a stack of open tags. */
|
||||
|
||||
class printer
|
||||
{
|
||||
public:
|
||||
printer (element &insertion_point);
|
||||
|
||||
void push_tag (std::string name,
|
||||
bool preserve_whitespace = false);
|
||||
void push_tag_with_class (std::string name,
|
||||
std::string class_,
|
||||
bool preserve_whitespace = false);
|
||||
void pop_tag ();
|
||||
|
||||
void set_attr (const char *name, std::string value);
|
||||
|
||||
void add_text (std::string text);
|
||||
|
||||
void add_raw (std::string text);
|
||||
|
||||
void push_element (std::unique_ptr<element> new_element);
|
||||
|
||||
void append (std::unique_ptr<node> new_node);
|
||||
|
||||
element *get_insertion_point () const;
|
||||
|
||||
private:
|
||||
// borrowed ptrs:
|
||||
std::vector<element *> m_open_tags;
|
||||
};
|
||||
|
||||
// RAII for push/pop element on xml::printer
|
||||
|
||||
class auto_print_element
|
||||
{
|
||||
public:
|
||||
auto_print_element (printer &printer,
|
||||
std::string name,
|
||||
bool preserve_whitespace = false)
|
||||
: m_printer (printer)
|
||||
{
|
||||
m_printer.push_tag (name, preserve_whitespace);
|
||||
}
|
||||
~auto_print_element ()
|
||||
{
|
||||
m_printer.pop_tag ();
|
||||
}
|
||||
|
||||
private:
|
||||
printer &m_printer;
|
||||
};
|
||||
|
||||
} // namespace xml
|
||||
|
||||
#endif /* GCC_XML_PRINTER_H. */
|
||||
113
gcc/xml.h
Normal file
113
gcc/xml.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Classes for representing XML trees.
|
||||
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/>. */
|
||||
|
||||
#ifndef GCC_XML_H
|
||||
#define GCC_XML_H
|
||||
|
||||
namespace xml {
|
||||
|
||||
// Forward decls; indentation reflects inheritance
|
||||
struct node;
|
||||
struct text;
|
||||
struct node_with_children;
|
||||
struct document;
|
||||
struct element;
|
||||
|
||||
struct node
|
||||
{
|
||||
virtual ~node () {}
|
||||
virtual void write_as_xml (pretty_printer *pp,
|
||||
int depth, bool indent) const = 0;
|
||||
virtual text *dyn_cast_text ()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void dump (FILE *out) const;
|
||||
void DEBUG_FUNCTION dump () const { dump (stderr); }
|
||||
};
|
||||
|
||||
struct text : public node
|
||||
{
|
||||
text (std::string str)
|
||||
: m_str (std::move (str))
|
||||
{}
|
||||
|
||||
void write_as_xml (pretty_printer *pp,
|
||||
int depth, bool indent) const final override;
|
||||
|
||||
text *dyn_cast_text () final override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
std::string m_str;
|
||||
};
|
||||
|
||||
struct node_with_children : public node
|
||||
{
|
||||
void add_child (std::unique_ptr<node> node);
|
||||
void add_text (std::string str);
|
||||
|
||||
std::vector<std::unique_ptr<node>> m_children;
|
||||
};
|
||||
|
||||
struct document : public node_with_children
|
||||
{
|
||||
void write_as_xml (pretty_printer *pp,
|
||||
int depth, bool indent) const final override;
|
||||
};
|
||||
|
||||
struct element : public node_with_children
|
||||
{
|
||||
element (std::string kind, bool preserve_whitespace)
|
||||
: m_kind (std::move (kind)),
|
||||
m_preserve_whitespace (preserve_whitespace)
|
||||
{}
|
||||
|
||||
void write_as_xml (pretty_printer *pp,
|
||||
int depth, bool indent) const final override;
|
||||
|
||||
void set_attr (const char *name, std::string value);
|
||||
|
||||
std::string m_kind;
|
||||
bool m_preserve_whitespace;
|
||||
std::map<std::string, std::string> m_attributes;
|
||||
std::vector<std::string> m_key_insertion_order;
|
||||
};
|
||||
|
||||
/* A fragment of raw XML source, to be spliced in directly.
|
||||
Use sparingly. */
|
||||
|
||||
struct raw : public node
|
||||
{
|
||||
raw (std::string xml_src)
|
||||
: m_xml_src (xml_src)
|
||||
{
|
||||
}
|
||||
|
||||
void write_as_xml (pretty_printer *pp,
|
||||
int depth, bool indent) const final override;
|
||||
|
||||
std::string m_xml_src;
|
||||
};
|
||||
|
||||
} // namespace xml
|
||||
|
||||
#endif /* GCC_XML_H. */
|
||||
Reference in New Issue
Block a user