aboutsummaryrefslogtreecommitdiff
path: root/gcc/diagnostic-format-html.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/diagnostic-format-html.cc')
-rw-r--r--gcc/diagnostic-format-html.cc1369
1 files changed, 965 insertions, 404 deletions
diff --git a/gcc/diagnostic-format-html.cc b/gcc/diagnostic-format-html.cc
index 6bb1caf..473880f 100644
--- a/gcc/diagnostic-format-html.cc
+++ b/gcc/diagnostic-format-html.cc
@@ -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"
@@ -30,208 +31,32 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic-format-text.h"
#include "diagnostic-output-file.h"
#include "diagnostic-buffer.h"
+#include "diagnostic-path.h"
+#include "diagnostic-client-data-hooks.h"
#include "selftest.h"
#include "selftest-diagnostic.h"
#include "pretty-print-format-impl.h"
#include "pretty-print-urlifier.h"
#include "edit-context.h"
#include "intl.h"
-
-namespace xml {
-
-/* Disable warnings about quoting issues in the pp_xxx calls below
- that (intentionally) don't follow GCC diagnostic conventions. */
-#if __GNUC__ >= 10
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wformat-diag"
-#endif
-
-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. */
-
-static void
-write_escaped_text (pretty_printer *pp, const char *text)
-{
- gcc_assert (text);
-
- for (const char *p = text; *p; ++p)
- {
- char ch = *p;
- switch (ch)
- {
- default:
- pp_character (pp, ch);
- break;
- case '\'':
- pp_string (pp, "&apos;");
- break;
- case '"':
- pp_string (pp, "&quot;");
- break;
- case '&':
- pp_string (pp, "&amp;");
- break;
- case '<':
- pp_string (pp, "&lt;");
- break;
- case '>':
- pp_string (pp, "&gt;");
- break;
- }
- }
-}
-
-/* struct node. */
-
-void
-node::dump (FILE *out) const
-{
- pretty_printer pp;
- pp.set_output_stream (out);
- write_as_xml (&pp, 0, true);
- pp_flush (&pp);
-}
-
-/* struct text : public node. */
-
-void
-text::write_as_xml (pretty_printer *pp, int /*depth*/, bool /*indent*/) const
-{
- write_escaped_text (pp, m_str.get ());
-}
-
-/* struct node_with_children : public node. */
-
-void
-node_with_children::add_child (std::unique_ptr<node> node)
-{
- gcc_assert (node.get ());
- m_children.push_back (std::move (node));
-}
-
-void
-node_with_children::add_text (label_text str)
-{
- gcc_assert (str.get ());
- add_child (std::make_unique <text> (std::move (str)));
-}
-
-
-/* struct document : public node_with_children. */
-
-void
-document::write_as_xml (pretty_printer *pp, int depth, bool indent) const
+#include "xml.h"
+#include "xml-printer.h"
+#include "diagnostic-state.h"
+#include "graphviz.h"
+#include "json.h"
+#include "selftest-xml.h"
+
+// struct html_generation_options
+
+html_generation_options::html_generation_options ()
+: m_css (true),
+ m_javascript (true),
+ m_show_state_diagrams (false),
+ m_show_state_diagram_xml (false),
+ m_show_state_diagram_dot_src (false)
{
- pp_string (pp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- pp_string (pp, "<!DOCTYPE html\n"
- " PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n"
- " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
- for (auto &iter : m_children)
- iter->write_as_xml (pp, depth, indent);
}
-/* struct element : public node_with_children. */
-
-void
-element::write_as_xml (pretty_printer *pp, int depth, bool indent) const
-{
- if (indent)
- {
- 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=\"", attr.first);
- write_escaped_text (pp, attr.second.get ());
- pp_string (pp, "\"");
- }
- if (m_children.empty ())
- pp_string (pp, " />");
- else
- {
- pp_string (pp, ">");
- for (auto &child : m_children)
- child->write_as_xml (pp, depth + 1, indent);
- if (indent)
- {
- pp_newline (pp);
- for (int i = 0; i < depth; ++i)
- pp_string (pp, " ");
- }
- pp_printf (pp, "</%s>", m_kind);
- }
-}
-
-void
-element::set_attr (const char *name, label_text value)
-{
- m_attributes[name] = std::move (value);
-}
-
-#if __GNUC__ >= 10
-# pragma GCC diagnostic pop
-#endif
-
-} // namespace xml
-
class html_builder;
/* Concrete buffering implementation subclass for HTML output. */
@@ -284,7 +109,11 @@ 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
+ set_main_input_filename (const char *name);
void on_report_diagnostic (const diagnostic_info &diagnostic,
diagnostic_t orig_diag_kind,
@@ -310,34 +139,60 @@ public:
make_element_for_metadata (const diagnostic_metadata &metadata);
std::unique_ptr<xml::element>
- make_element_for_source (const diagnostic_info &diagnostic);
+ make_element_for_patch (const diagnostic_info &diagnostic);
- std::unique_ptr<xml::element>
- make_element_for_path (const diagnostic_path &path);
+ void add_focus_id (std::string focus_id)
+ {
+ m_ui_focus_ids.append_string (focus_id.c_str ());
+ }
- std::unique_ptr<xml::element>
- make_element_for_patch (const diagnostic_info &diagnostic);
+ std::unique_ptr<xml::node>
+ maybe_make_state_diagram (const diagnostic_event &event);
private:
+ void
+ add_stylesheet (std::string url);
+
std::unique_ptr<xml::element>
make_element_for_diagnostic (const diagnostic_info &diagnostic,
- diagnostic_t orig_diag_kind);
+ diagnostic_t orig_diag_kind,
+ bool alert);
std::unique_ptr<xml::element>
make_metadata_element (label_text label,
label_text url);
+ void
+ add_at_nesting_level (size_t nesting_level,
+ std::unique_ptr<xml::element> child_diag_element);
+
+ void
+ push_nesting_level ();
+
+ void
+ pop_nesting_level ();
+
diagnostic_context &m_context;
pretty_printer *m_printer;
const line_maps *m_line_maps;
+ html_generation_options m_html_gen_opts;
+ const logical_location_manager *m_logical_loc_mgr;
std::unique_ptr<xml::document> m_document;
+ xml::element *m_head_element;
+ xml::element *m_title_element;
xml::element *m_diagnostics_element;
std::unique_ptr<xml::element> m_cur_diagnostic_element;
+ std::vector<xml::element *> m_cur_nesting_levels;
+ int m_next_diag_id; // for handing out unique IDs
+ json::array m_ui_focus_ids;
+ logical_location m_last_logical_location;
+ location_t m_last_location;
+ expanded_location m_last_expanded_location;
};
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 +200,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,48 +255,230 @@ diagnostic_html_format_buffer::flush ()
/* class html_builder. */
+/* Style information for writing out HTML paths.
+ Colors taken from https://pf3.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"
+ " background-color: white;\n"
+ " white-space: pre; }\n"
+ " .annotation { color: green;\n"
+ " background-color: white;\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"
+ " .highlight-a { color: #703fec;\n" // pf-purple-400
+ " font-weight: bold; }\n"
+ " .highlight-b { color: #3f9c35;\n" // pf-green-400
+ " font-weight: bold; }\n"
+ " .gcc-quoted-text { font-weight: bold;\n"
+ " font-family: mono; }\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 get_any_state_diagram (focus_idx)\n"
+ " {\n"
+ " const element_id = focus_ids[focus_idx];\n"
+ " return document.getElementById(element_id + \"-state-diagram\");\n"
+ " }\n"
+ " function unhighlight_current_focus_idx ()\n"
+ " {\n"
+ " get_focus_span (current_focus_idx).classList.remove ('selected');\n"
+ " state_diagram = get_any_state_diagram (current_focus_idx);\n"
+ " if (state_diagram) {\n"
+ " state_diagram.style.visibility = \"hidden\";\n"
+ " }\n"
+ " }\n"
+ " function highlight_current_focus_idx ()\n"
+ " {\n"
+ " const el = get_focus_span (current_focus_idx);\n"
+ " el.classList.add ('selected');\n"
+ " state_diagram = get_any_state_diagram (current_focus_idx);\n"
+ " if (state_diagram) {\n"
+ " state_diagram.style.visibility = \"visible\";\n"
+ " }\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");
+
+struct html_doctypedecl : public xml::doctypedecl
+{
+ void write_as_xml (pretty_printer *pp,
+ int depth, bool indent) const final override
+ {
+ if (indent)
+ {
+ for (int i = 0; i < depth; ++i)
+ pp_string (pp, " ");
+ }
+ 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);
+ }
+};
+
/* 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_logical_loc_mgr (nullptr),
+ m_head_element (nullptr),
+ m_title_element (nullptr),
+ m_diagnostics_element (nullptr),
+ m_next_diag_id (0),
+ m_last_location (UNKNOWN_LOCATION),
+ m_last_expanded_location ({})
{
gcc_assert (m_line_maps);
+ if (auto client_data_hooks = context.get_client_data_hooks ())
+ m_logical_loc_mgr = client_data_hooks->get_logical_location_manager ();
+
m_document = std::make_unique<xml::document> ();
+ m_document->m_doctypedecl = std::make_unique<html_doctypedecl> ();
{
auto html_element = std::make_unique<xml::element> ("html", false);
- html_element->set_attr
- ("xmlns",
- label_text::borrow ("http://www.w3.org/1999/xhtml"));
+ 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));
+
{
+ xml::auto_print_element head (xp, "head");
+ m_head_element = xp.get_insertion_point ();
{
- auto head_element = std::make_unique<xml::element> ("head", false);
+ xml::auto_print_element title (xp, "title", true);
+ m_title_element = xp.get_insertion_point ();
+ m_title_element->add_text (" ");
+ }
+
+ if (m_html_gen_opts.m_css)
{
- 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));
+ add_stylesheet ("https://cdnjs.cloudflare.com/ajax/libs/patternfly/3.24.0/css/patternfly.min.css");
+ add_stylesheet ("https://cdnjs.cloudflare.com/ajax/libs/patternfly/3.24.0/css/patternfly-additions.min.css");
+ xp.add_raw (HTML_STYLE);
}
- html_element->add_child (std::move (head_element));
-
- auto body_element = std::make_unique<xml::element> ("body", false);
+ 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));
}
}
+void
+html_builder::set_main_input_filename (const char *name)
+{
+ gcc_assert (m_title_element);
+ if (name)
+ {
+ m_title_element->m_children.clear ();
+ m_title_element->add_text (name);
+ }
+}
+
+void
+html_builder::add_stylesheet (std::string url)
+{
+ gcc_assert (m_head_element);
+
+ xml::printer xp (*m_head_element);
+ xp.push_tag ("link", false);
+ xp.set_attr ("rel", "stylesheet");
+ xp.set_attr ("type", "text/css");
+ xp.set_attr ("href", std::move (url));
+}
+
/* Implementation of "on_report_diagnostic" for HTML output. */
void
@@ -459,8 +496,14 @@ html_builder::on_report_diagnostic (const diagnostic_info &diagnostic,
fnotice (stderr, "Internal compiler error:\n");
}
+ const int nesting_level = m_context.get_diagnostic_nesting_level ();
+ bool alert = true;
+ if (m_cur_diagnostic_element && nesting_level > 0)
+ alert = false;
+ if (!m_cur_diagnostic_element)
+ m_last_logical_location = logical_location ();
auto diag_element
- = make_element_for_diagnostic (diagnostic, orig_diag_kind);
+ = make_element_for_diagnostic (diagnostic, orig_diag_kind, alert);
if (buffer)
{
gcc_assert (!m_cur_diagnostic_element);
@@ -469,121 +512,501 @@ html_builder::on_report_diagnostic (const diagnostic_info &diagnostic,
else
{
if (m_cur_diagnostic_element)
- /* Nested diagnostic. */
- m_cur_diagnostic_element->add_child (std::move (diag_element));
+ {
+ /* Nested diagnostic. */
+ gcc_assert (nesting_level >= 0);
+ add_at_nesting_level (nesting_level, std::move (diag_element));
+ }
else
/* Top-level diagnostic. */
- m_cur_diagnostic_element = std::move (diag_element);
+ {
+ m_cur_diagnostic_element = std::move (diag_element);
+ m_cur_nesting_levels.clear ();
+ }
}
}
-std::unique_ptr<xml::element>
-html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
- diagnostic_t orig_diag_kind)
+// For ease of comparison with experimental-nesting-show-levels=yes
+
+static void
+add_nesting_level_attr (xml::element &element,
+ int nesting_level)
{
- class html_token_printer : public token_printer
- {
- public:
- html_token_printer (html_builder &builder,
- xml::element &parent_element)
- : m_builder (builder)
+ element.set_attr ("nesting-level", std::to_string (nesting_level));
+}
+
+void
+html_builder::
+add_at_nesting_level (size_t nesting_level,
+ std::unique_ptr<xml::element> child_diag_element)
+{
+ gcc_assert (m_cur_diagnostic_element);
+ while (nesting_level > m_cur_nesting_levels.size ())
+ push_nesting_level ();
+ while (nesting_level < m_cur_nesting_levels.size ())
+ pop_nesting_level ();
+
+ if (nesting_level > 0)
{
- m_open_elements.push_back (&parent_element);
+ gcc_assert (!m_cur_nesting_levels.empty ());
+ auto current_nesting_level = m_cur_nesting_levels.back ();
+ xml::printer xp (*current_nesting_level);
+ xp.push_tag ("li");
+ add_nesting_level_attr (*xp.get_insertion_point (),
+ m_cur_nesting_levels.size ());
+ xp.append (std::move (child_diag_element));
+ xp.pop_tag ("li");
}
- void print_tokens (pretty_printer */*pp*/,
- const pp_token_list &tokens) final override
+ else
+ m_cur_diagnostic_element->add_child (std::move (child_diag_element));
+}
+
+void
+html_builder::push_nesting_level ()
+{
+ gcc_assert (m_cur_diagnostic_element);
+ auto new_nesting_level = std::make_unique<xml::element> ("ul", false);
+ add_nesting_level_attr (*new_nesting_level,
+ m_cur_nesting_levels.size () + 1);
+ xml::element *current_nesting_level = nullptr;
+ if (!m_cur_nesting_levels.empty ())
+ current_nesting_level = m_cur_nesting_levels.back ();
+ m_cur_nesting_levels.push_back (new_nesting_level.get ());
+ if (current_nesting_level)
+ current_nesting_level->add_child (std::move (new_nesting_level));
+ else
+ m_cur_diagnostic_element->add_child (std::move (new_nesting_level));
+}
+
+void
+html_builder::pop_nesting_level ()
+{
+ gcc_assert (m_cur_diagnostic_element);
+ m_cur_nesting_levels.pop_back ();
+}
+
+static void
+print_pre_source (xml::printer &xp, const char *text)
+{
+ xp.push_tag_with_class ("pre", "source", true);
+ xp.add_text (text);
+ xp.pop_tag ("pre");
+}
+
+std::unique_ptr<xml::node>
+html_builder::maybe_make_state_diagram (const diagnostic_event &event)
+{
+ if (!m_html_gen_opts.m_show_state_diagrams)
+ return nullptr;
+
+ /* Get XML state document; if we're going to print it later, also request
+ the debug version. */
+ auto xml_state
+ = event.maybe_make_xml_state (m_html_gen_opts.m_show_state_diagram_xml);
+ if (!xml_state)
+ return nullptr;
+
+ // Convert it to .dot AST
+ auto graph = make_dot_graph_from_xml_state (*xml_state);
+ gcc_assert (graph);
+
+ auto wrapper = std::make_unique<xml::element> ("div", false);
+ xml::printer xp (*wrapper);
+
+ if (m_html_gen_opts.m_show_state_diagram_xml)
{
- /* Implement print_tokens by adding child elements to
- m_parent_element. */
- for (auto iter = tokens.m_first; iter; iter = iter->m_next)
- switch (iter->m_kind)
- {
- default:
- gcc_unreachable ();
-
- case pp_token::kind::text:
- {
- 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 ())));
- }
- break;
+ // For debugging, show the XML src inline:
+ pretty_printer pp;
+ xml_state->write_as_xml (&pp, 0, true);
+ print_pre_source (xp, pp_formatted_text (&pp));
+ }
+
+ if (m_html_gen_opts.m_show_state_diagram_dot_src)
+ {
+ // For debugging, show the dot src inline:
+ pretty_printer pp;
+ dot::writer w (pp);
+ graph->print (w);
+ print_pre_source (xp, pp_formatted_text (&pp));
+ }
- case pp_token::kind::begin_color:
- case pp_token::kind::end_color:
- /* These are no-ops. */
- break;
+ // Turn the .dot into SVG and splice into place
+ auto svg = dot::make_svg_from_graph (*graph);
+ if (svg)
+ xp.append (std::move (svg));
- 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")));
- }
- break;
- case pp_token::kind::end_quote:
- {
- pop_element ();
- insertion_element ().add_text (label_text::borrow (close_quote));
- }
- break;
-
- case pp_token::kind::begin_url:
- {
- 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));
- push_element (std::move (anchor));
- }
- break;
- case pp_token::kind::end_url:
- pop_element ();
- break;
- }
+ return wrapper;
+}
+
+/* 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 diagnostic_path &path,
+ const std::string &event_id_prefix)
+ : m_xp (xp),
+ m_html_builder (builder),
+ m_path (path),
+ m_event_id_prefix (event_id_prefix),
+ m_next_event_idx (0),
+ m_curr_event_id ()
+ {
+ }
+
+ void begin_label () final override
+ {
+ m_curr_event_id = m_next_event_idx++;
+ m_xp.push_tag_with_class ("span", "event", true);
+ m_xp.set_attr ("id", get_element_id ());
+ m_html_builder.add_focus_id (get_element_id ());
+ }
+
+ void end_label () final override
+ {
+ const diagnostic_event &event
+ = m_path.get_event (m_curr_event_id.zero_based ());
+ if (auto state_doc = m_html_builder.maybe_make_state_diagram (event))
+ {
+ m_xp.push_tag_with_class ("div", "state-diagram", false);
+ m_xp.set_attr ("id", get_element_id () + "-state-diagram");
+ m_xp.set_attr ("style",
+ ("position: absolute;"
+ " z-index: 1;"
+ " visibility: hidden;"));
+ m_xp.append (std::move (state_doc));
+ m_xp.pop_tag ("div");
}
- private:
- xml::element &insertion_element () const
+ m_xp.pop_tag ("span"); // from begin_label
+ }
+
+private:
+ std::string
+ get_element_id () const
+ {
+ gcc_assert (m_curr_event_id.known_p ());
+ return (m_event_id_prefix
+ + std::to_string (m_curr_event_id.zero_based ()));
+ }
+
+ xml::printer &m_xp;
+ html_builder &m_html_builder;
+ const diagnostic_path &m_path;
+ const std::string &m_event_id_prefix;
+ int m_next_event_idx;
+ diagnostic_event_id_t m_curr_event_id;
+};
+
+/* See https://pf3.patternfly.org/v3/pattern-library/widgets/#alerts */
+static const char *
+get_pf_class_for_alert_div (diagnostic_t diag_kind)
+{
+ switch (diag_kind)
{
- return *m_open_elements.back ();
+ case DK_DEBUG:
+ case DK_NOTE:
+ return "alert alert-info";
+
+ case DK_ANACHRONISM:
+ case DK_WARNING:
+ return "alert alert-warning";
+
+ case DK_ERROR:
+ case DK_SORRY:
+ case DK_ICE:
+ case DK_ICE_NOBT:
+ case DK_FATAL:
+ return "alert alert-danger";
+
+ default:
+ gcc_unreachable ();
}
- void push_element (std::unique_ptr<xml::element> new_element)
+}
+
+static const char *
+get_pf_class_for_alert_icon (diagnostic_t diag_kind)
+{
+ switch (diag_kind)
{
- xml::element &current_top = insertion_element ();
- m_open_elements.push_back (new_element.get ());
- current_top.add_child (std::move (new_element));
+ case DK_DEBUG:
+ case DK_NOTE:
+ return "pficon pficon-info";
+
+ case DK_ANACHRONISM:
+ case DK_WARNING:
+ return "pficon pficon-warning-triangle-o";
+
+ case DK_ERROR:
+ case DK_SORRY:
+ case DK_ICE:
+ case DK_ICE_NOBT:
+ case DK_FATAL:
+ return "pficon pficon-error-circle-o";
+
+ default:
+ gcc_unreachable ();
}
- void pop_element ()
+}
+
+static const char *
+get_label_for_logical_location_kind (enum logical_location_kind kind)
+{
+ switch (kind)
{
- m_open_elements.pop_back ();
+ default:
+ gcc_unreachable ();
+ case logical_location_kind::unknown:
+ return nullptr;
+
+ /* Kinds within executable code. */
+ case logical_location_kind::function:
+ return "Function";
+ case logical_location_kind::member:
+ return "Member";
+ case logical_location_kind::module_:
+ return "Module";
+ case logical_location_kind::namespace_:
+ return "Namespace";
+ case logical_location_kind::type:
+ return "Type";
+ case logical_location_kind::return_type:
+ return "Return type";
+ case logical_location_kind::parameter:
+ return "Parameter";
+ case logical_location_kind::variable:
+ return "Variable";
+
+ /* Kinds within XML or HTML documents. */
+ case logical_location_kind::element:
+ return "Element";
+ case logical_location_kind::attribute:
+ return "Attribute";
+ case logical_location_kind::text:
+ return "Text";
+ case logical_location_kind::comment:
+ return "Comment";
+ case logical_location_kind::processing_instruction:
+ return "Processing Instruction";
+ case logical_location_kind::dtd:
+ return "DTD";
+ case logical_location_kind::declaration:
+ return "Declaration";
+
+ /* Kinds within JSON documents. */
+ case logical_location_kind::object:
+ return "Object";
+ case logical_location_kind::array:
+ return "Array";
+ case logical_location_kind::property:
+ return "Property";
+ case logical_location_kind::value:
+ return "Value";
}
+}
- html_builder &m_builder;
- /* We maintain a stack of currently "open" elements.
- Children are added to the topmost open element. */
- std::vector<xml::element *> m_open_elements;
- };
+static void
+add_labelled_value (xml::printer &xp,
+ std::string id,
+ std::string label,
+ std::string value,
+ bool quote_value)
+{
+ xp.push_tag ("div", true);
+ xp.set_attr ("id", id);
+ xp.push_tag ("span");
+ xp.add_text (label);
+ xp.add_text (" ");
+ xp.pop_tag ("span");
+ xp.push_tag ("span");
+ if (quote_value)
+ xp.set_attr ("class", "gcc-quoted-text");
+ xp.add_text (std::move (value));
+ xp.pop_tag ("span");
+ xp.pop_tag ("div");
+}
- auto diag_element = make_div (label_text::borrow ("gcc-diagnostic"));
+class html_token_printer : public token_printer
+{
+public:
+ html_token_printer (xml::element &parent_element)
+ /* Ideally pp_token_lists that reach a token_printer should be
+ "balanced", but for now they can have mismatching pp_tokens
+ e.g. a begin_color without an end_color (PR other/120610).
+ Give html_token_printer its own xml::printer as a firewall to
+ limit the scope of the mismatches in the HTML. */
+ : m_xp (parent_element,
+ /* Similarly we don't check that the popped tags match. */
+ false)
+ {
+ }
+ void print_tokens (pretty_printer */*pp*/,
+ const pp_token_list &tokens) final override
+ {
+ /* Implement print_tokens by adding child elements to
+ m_parent_element. */
+ for (auto iter = tokens.m_first; iter; iter = iter->m_next)
+ switch (iter->m_kind)
+ {
+ default:
+ gcc_unreachable ();
+
+ case pp_token::kind::text:
+ {
+ pp_token_text *sub = as_a <pp_token_text *> (iter);
+ /* The value might be in the obstack, so we may need to
+ copy it. */
+ m_xp.add_text (sub->m_value.get ());
+ }
+ break;
+
+ case pp_token::kind::begin_color:
+ {
+ pp_token_begin_color *sub = as_a <pp_token_begin_color *> (iter);
+ gcc_assert (sub->m_value.get ());
+ m_xp.push_tag_with_class ("span", sub->m_value.get ());
+ }
+ break;
+
+ case pp_token::kind::end_color:
+ m_xp.pop_tag ("span");
+ break;
+
+ case pp_token::kind::begin_quote:
+ {
+ m_xp.add_text (open_quote);
+ m_xp.push_tag_with_class ("span", "gcc-quoted-text");
+ }
+ break;
+ case pp_token::kind::end_quote:
+ {
+ m_xp.pop_tag ("span");
+ m_xp.add_text (close_quote);
+ }
+ break;
+
+ case pp_token::kind::begin_url:
+ {
+ pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);
+ m_xp.push_tag ("a", true);
+ m_xp.set_attr ("href", sub->m_value.get ());
+ }
+ break;
+ case pp_token::kind::end_url:
+ m_xp.pop_tag ("a");
+ break;
+
+ case pp_token::kind::event_id:
+ {
+ pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
+ gcc_assert (sub->m_event_id.known_p ());
+ m_xp.add_text ("(");
+ m_xp.add_text (std::to_string (sub->m_event_id.one_based ()));
+ m_xp.add_text (")");
+ }
+ break;
+ }
+ }
+
+private:
+ xml::printer m_xp;
+};
+
+/* Make a <div class="gcc-diagnostic"> for DIAGNOSTIC.
+
+ If ALERT is true, make it be a PatternFly alert (see
+ https://pf3.patternfly.org/v3/pattern-library/widgets/#alerts) and
+ show severity text (e.g. "error: ").
+
+ If ALERT is false, don't show the severity text and don't show
+ the filename if it's the same as the previous diagnostic within the
+ diagnostic group. */
+
+std::unique_ptr<xml::element>
+html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
+ diagnostic_t orig_diag_kind,
+ bool alert)
+{
+ 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);
+ }
// TODO: might be nice to emulate the text output format, but colorize it
- auto message_span = make_span (label_text::borrow ("gcc-message"));
- html_token_printer tok_printer (*this, *message_span.get ());
+ /* See https://pf3.patternfly.org/v3/pattern-library/widgets/#alerts
+ which has this example:
+<div class="alert alert-danger">
+ <span class="pficon pficon-error-circle-o"></span>
+ <strong>Hey there is a problem!</strong> Yeah this is really messed up and you should <a href="#" class="alert-link">know about it</a>.
+</div>
+ */
+ auto diag_element = make_div ("gcc-diagnostic");
+ diag_element->set_attr ("id", diag_id);
+ if (alert)
+ diag_element->set_attr ("class",
+ get_pf_class_for_alert_div (diagnostic.kind));
+
+ xml::printer xp (*diag_element.get ());
+ const size_t depth_within_alert_div = 1;
+
+ gcc_assert (xp.get_num_open_tags () == depth_within_alert_div);
+
+ if (alert)
+ {
+ xp.push_tag_with_class ("span",
+ get_pf_class_for_alert_icon (diagnostic.kind),
+ true);
+ xp.add_text (" ");
+ xp.pop_tag ("span");
+ }
+
+ // The rest goes in the <div>...
+ gcc_assert (xp.get_num_open_tags () == depth_within_alert_div);
+
+ xp.push_tag_with_class ("div", "gcc-message", true);
+ std::string message_alert_id (diag_id + "-message");
+ xp.set_attr ("id", message_alert_id);
+ add_focus_id (message_alert_id);
+
+ const size_t depth_within_message_div = depth_within_alert_div + 1;
+ gcc_assert (xp.get_num_open_tags () == depth_within_message_div);
+
+ // Severity e.g. "warning: "
+ bool show_severity = true;
+ if (!alert)
+ show_severity = false;
+ if (show_severity)
+ {
+ xp.push_tag ("strong");
+ xp.add_text (_(get_diagnostic_kind_text (diagnostic.kind)));
+ xp.pop_tag ("strong");
+ xp.add_text (" ");
+ }
+
+ // Add the message itself:
+ html_token_printer tok_printer (*xp.get_insertion_point ());
m_printer->set_token_printer (&tok_printer);
pp_output_formatted_text (m_printer, m_context.get_urlifier ());
m_printer->set_token_printer (nullptr);
pp_clear_output_area (m_printer);
- diag_element->add_child (std::move (message_span));
+ // Add any metadata as a suffix to the message
if (diagnostic.metadata)
{
- diag_element->add_text (label_text::borrow (" "));
- diag_element->add_child
- (make_element_for_metadata (*diagnostic.metadata));
+ xp.add_text (" ");
+ xp.append (make_element_for_metadata (*diagnostic.metadata));
}
+ // Add any option as a suffix to the message
+
label_text option_text = label_text::take
(m_context.make_option_name (diagnostic.option_id,
orig_diag_kind, diagnostic.kind));
@@ -592,80 +1015,128 @@ 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 ("["));
+ xp.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));
+ xp.append (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));
+ gcc_assert (xp.get_num_open_tags () == depth_within_message_div);
- /* 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));
+ xp.pop_tag ("div");
- if (auto patch_element = make_element_for_patch (diagnostic))
- diag_element->add_child (std::move (patch_element));
+ gcc_assert (xp.get_num_open_tags () == depth_within_alert_div);
- return diag_element;
-}
+ /* Show any logical location. */
+ if (m_logical_loc_mgr)
+ if (auto client_data_hooks = m_context.get_client_data_hooks ())
+ if (auto logical_loc = client_data_hooks->get_current_logical_location ())
+ if (logical_loc != m_last_logical_location)
+ {
+ enum logical_location_kind kind
+ = m_logical_loc_mgr->get_kind (logical_loc);;
+ if (const char *label = get_label_for_logical_location_kind (kind))
+ if (const char *name_with_scope
+ = m_logical_loc_mgr->get_name_with_scope (logical_loc))
+ add_labelled_value (xp, "logical-location",
+ label, name_with_scope, true);
+ m_last_logical_location = logical_loc;
+ }
-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);
+ /* Show any physical location. */
+ const expanded_location s
+ = diagnostic_expand_location (&diagnostic);
+ if (s != m_last_expanded_location
+ || alert)
+ {
+ if (s.file
+ && (s.file != m_last_expanded_location.file
+ || alert))
+ add_labelled_value (xp, "file", "File", s.file, false);
+ if (s.line)
+ {
+ add_labelled_value (xp, "line", "Line", std::to_string (s.line), false);
+ diagnostic_column_policy column_policy (m_context);
+ int converted_column = column_policy.converted_column (s);
+ if (converted_column >= 0)
+ add_labelled_value (xp, "column", "Column",
+ std::to_string (converted_column),
+ false);
+ }
+ if (s.file)
+ m_last_expanded_location = s;
+ }
- if (strlen (text.get ()) == 0)
- return nullptr;
+ /* Source (and fix-it hints). */
+ {
+ // TODO: m_context.m_last_location should be moved into the sink
+ location_t saved = m_context.m_last_location;
+ m_context.m_last_location = m_last_location;
+ m_context.maybe_show_locus_as_html (*diagnostic.richloc,
+ m_context.m_source_printing,
+ diagnostic.kind,
+ xp,
+ nullptr,
+ nullptr);
+ m_context.m_last_location = saved;
+ m_last_location = m_context.m_last_location;
+ }
- 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;
-}
+ gcc_assert (xp.get_num_open_tags () == depth_within_alert_div);
-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;
+ /* Execution path. */
+ if (auto path = diagnostic.richloc->get_path ())
+ {
+ xp.push_tag ("div");
+ xp.set_attr ("id", "execution-path");
+
+ xp.push_tag ("label", true);
+ const int num_events = path->num_events ();
+ pretty_printer pp;
+ pp_printf_n (&pp, num_events,
+ "Execution path with %i event",
+ "Execution path with %i events",
+ num_events);
+ xp.add_text_from_pp (pp);
+ xp.pop_tag ("label");
+
+ std::string event_id_prefix (diag_id + "-event-");
+ html_path_label_writer event_label_writer (xp, *this, *path,
+ event_id_prefix);
+
+ diagnostic_source_print_policy dspp (m_context);
+ print_path_as_html (xp, *path, m_context, &event_label_writer,
+ dspp);
+
+ xp.pop_tag ("div");
+ }
+
+ gcc_assert (xp.get_num_open_tags () == depth_within_alert_div);
- 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;
+ if (auto patch_element = make_element_for_patch (diagnostic))
+ {
+ xp.push_tag ("div");
+ xp.set_attr ("id", "suggested-fix");
+ xp.push_tag ("label", true);
+ xp.add_text ("Suggested fix");
+ xp.pop_tag ("label");
+ xp.append (std::move (patch_element));
+ xp.pop_tag ("div");
+ }
+
+ return diag_element;
}
std::unique_ptr<xml::element>
@@ -674,13 +1145,18 @@ 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 +1164,27 @@ 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));
+ if (url.get ())
+ {
+ xp.push_tag ("a", true);
+ xp.set_attr ("href", url.get ());
+ }
+ xp.add_text (label.get ());
+ if (url.get ())
+ xp.pop_tag ("a");
}
- 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 +1218,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 +1238,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");
@@ -781,6 +1276,12 @@ public:
diagnostic_output_format::dump (out, indent);
}
+ void
+ set_main_input_filename (const char *name) final override
+ {
+ m_builder.set_main_input_filename (name);
+ }
+
std::unique_ptr<diagnostic_per_format_buffer>
make_per_format_buffer () final override
{
@@ -811,7 +1312,7 @@ public:
{
m_builder.emit_diagram (diagram);
}
- void after_diagnostic (const diagnostic_info &)
+ void after_diagnostic (const diagnostic_info &) final override
{
/* No-op, but perhaps could show paths here. */
}
@@ -842,9 +1343,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 +1359,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 +1425,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;
@@ -936,6 +1441,62 @@ make_html_sink (diagnostic_context &context,
namespace selftest {
+/* Helper for writing tests of html_token_printer.
+ Printing to m_pp will appear as HTML within m_top_element, a <div>. */
+
+struct token_printer_test
+{
+ token_printer_test ()
+ : m_top_element ("div", true),
+ m_tok_printer (m_top_element)
+ {
+ m_pp.set_token_printer (&m_tok_printer);
+ }
+
+ xml::element m_top_element;
+ html_token_printer m_tok_printer;
+ pretty_printer m_pp;
+};
+
+static void
+test_token_printer ()
+{
+ {
+ token_printer_test t;
+ pp_printf (&t.m_pp, "hello world");
+ ASSERT_XML_PRINT_EQ
+ (t.m_top_element,
+ "<div>hello world</div>\n");
+ }
+
+ {
+ token_printer_test t;
+ pp_printf (&t.m_pp, "%qs: %qs", "foo", "bar");
+ ASSERT_XML_PRINT_EQ
+ (t.m_top_element,
+ "<div>"
+ "`"
+ "<span class=\"gcc-quoted-text\">"
+ "foo"
+ "</span>"
+ "&apos;: `"
+ "<span class=\"gcc-quoted-text\">"
+ "bar"
+ "</span>"
+ "&apos;"
+ "</div>\n");
+ }
+
+ {
+ token_printer_test t;
+ diagnostic_event_id_t event_id (0);
+ pp_printf (&t.m_pp, "foo %@ bar", &event_id);
+ ASSERT_XML_PRINT_EQ
+ (t.m_top_element,
+ "<div>foo (1) bar</div>\n");
+ }
+}
+
/* A subclass of html_output_format for writing selftests.
The XML output is cached internally, rather than written
out to a file. */
@@ -945,9 +1506,14 @@ 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 ();
+ sink->set_main_input_filename ("(main input filename)");
m_format = sink.get (); // borrowed
set_output_format (std::move (sink));
@@ -968,8 +1534,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
@@ -995,26 +1562,25 @@ test_simple_log ()
const xml::document &doc = dc.get_document ();
- pretty_printer pp;
- doc.write_as_xml (&pp, 0, true);
- ASSERT_STREQ
- (pp_formatted_text (&pp),
+ ASSERT_XML_PRINT_EQ
+ (doc,
("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE html\n"
" PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n"
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
" <head>\n"
- " <title>Title goes here</title>\n"
+ " <title>(main input filename)</title>\n"
" </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>&apos;</span>\n"
+ " <div class=\"alert alert-danger\" id=\"gcc-diag-0\">\n"
+ " <span class=\"pficon pficon-error-circle-o\"> </span>\n"
+ " <div class=\"gcc-message\" id=\"gcc-diag-0-message\"><strong>error: </strong> this is a test: `<span class=\"gcc-quoted-text\">foo</span>&apos;</div>\n"
" </div>\n"
" </div>\n"
" </body>\n"
- "</html>"));
+ "</html>\n"));
}
static void
@@ -1027,11 +1593,8 @@ test_metadata ()
diagnostic_metadata metadata;
metadata.add_cwe (415);
auto element = b.make_element_for_metadata (metadata);
- pretty_printer pp;
- element->write_as_xml (&pp, 0, true);
- ASSERT_STREQ
- (pp_formatted_text (&pp),
- "\n"
+ ASSERT_XML_PRINT_EQ
+ (*element,
"<span class=\"gcc-metadata\">"
"<span class=\"gcc-metadata-item\">"
"["
@@ -1040,7 +1603,7 @@ test_metadata ()
"</a>"
"]"
"</span>"
- "</span>");
+ "</span>\n");
}
{
@@ -1049,11 +1612,8 @@ test_metadata ()
"http://example.com");
metadata.add_rule (rule);
auto element = b.make_element_for_metadata (metadata);
- pretty_printer pp;
- element->write_as_xml (&pp, 0, true);
- ASSERT_STREQ
- (pp_formatted_text (&pp),
- "\n"
+ ASSERT_XML_PRINT_EQ
+ (*element,
"<span class=\"gcc-metadata\">"
"<span class=\"gcc-metadata-item\">"
"["
@@ -1062,7 +1622,7 @@ test_metadata ()
"</a>"
"]"
"</span>"
- "</span>");
+ "</span>\n");
}
}
@@ -1072,6 +1632,7 @@ void
diagnostic_format_html_cc_tests ()
{
auto_fix_quotes fix_quotes;
+ test_token_printer ();
test_simple_log ();
test_metadata ();
}