/* Escaping rules are different for HTML <script> elements,
so add the script "raw" for now. */
xp.add_raw (HTML_SCRIPT);
- xp.pop_tag (); // script
+ xp.pop_tag ("script");
}
}
void end_label () final override
{
- m_xp.pop_tag (); // span
+ m_xp.pop_tag ("span"); // from begin_label
}
private:
class html_token_printer : public token_printer
{
public:
- html_token_printer (xml::printer &xp)
- : m_xp (xp)
+ 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*/,
break;
case pp_token::kind::end_color:
- m_xp.pop_tag ();
+ m_xp.pop_tag ("span");
break;
case pp_token::kind::begin_quote:
break;
case pp_token::kind::end_quote:
{
- m_xp.pop_tag ();
+ m_xp.pop_tag ("span");
m_xp.add_text (close_quote);
}
break;
}
break;
case pp_token::kind::end_url:
- m_xp.pop_tag ();
+ m_xp.pop_tag ("a");
break;
}
}
private:
- xml::printer &m_xp;
+ xml::printer m_xp;
};
auto diag_element = make_div ("gcc-diagnostic");
add_focus_id (message_span_id);
xml::printer xp (*message_span.get ());
- html_token_printer tok_printer (xp);
+ 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);
}
xp.add_text (label.get ());
if (url.get ())
- xp.pop_tag ();
+ xp.pop_tag ("a");
}
xp.add_text ("]");
return item;
m_ui_focus_ids.print (&pp, true);
pp_string (&pp, ";\n");
xp.add_raw (pp_formatted_text (&pp));
- xp.pop_tag (); // script
+ xp.pop_tag ("script");
}
auto top = m_document.get ();
top->dump (outf);
{
xp.push_tag_with_class ("td", "interprocmargin", false);
xp.set_attr ("style", "padding-left: 100px");
- xp.pop_tag ();
+ xp.pop_tag ("td");
}
xp.push_tag_with_class ("td", "stack-frame", false);
label_text funcname
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
+ xp.pop_tag ("span");
+ xp.pop_tag ("div");
}
}
return std::make_unique<stack_frame> (std::move (parent),
auto parent = std::move (frame->m_parent);
if (frame->m_logical_loc)
{
- xp.pop_tag (); // td
- xp.pop_tag (); // tr
- xp.pop_tag (); // table
+ xp.pop_tag ("td");
+ xp.pop_tag ("tr");
+ xp.pop_tag ("table");
}
return parent;
}
{
xp.push_tag_with_class ("span", "funcname", true);
xp.add_text (funcname.get ());
- xp.pop_tag (); //span
+ xp.pop_tag ("span");
xp.add_text (": ");
}
}
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
+ xp.pop_tag ("span");
}
if (show_depths)
{
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 ("span");
}
- xp.pop_tag (); // div
+ xp.pop_tag ("div");
/* Print a run of events. */
thread_event_printer &tep = thread_event_printers[swimlane_idx];
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
+ 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
+ xp.pop_tag ("div");
}
} /* end of anonymous namespace for path-printing code. */
{
friend class layout_printer<to_text>;
+ // This is a RAII class for HTML, but is a no-op for text.
+ struct auto_check_tag_nesting
+ {
+ auto_check_tag_nesting (to_text &) {}
+ };
+
to_text (pretty_printer &pp,
colorizer &colorizer)
: m_pp (pp),
{
// no-op for text
}
- void pop_html_tag (std::string)
+ void pop_html_tag (const char *)
{
// no-op for text
}
{
friend class layout_printer<to_html>;
+ // RAII class for ensuring that the tags nested correctly
+ struct auto_check_tag_nesting : public xml::auto_check_tag_nesting
+ {
+ public:
+ auto_check_tag_nesting (to_html &sink)
+ : xml::auto_check_tag_nesting (sink.m_xp)
+ {
+ }
+ };
+
to_html (xml::printer &xp,
const rich_location *richloc,
html_label_writer *html_label_writer)
preserve_whitespace);
}
- void pop_html_tag (std::string /*name*/)
+ void pop_html_tag (const char *expected_name)
{
- m_xp.pop_tag ();
+ m_xp.pop_tag (expected_name);
}
void add_html_tag_with_class (std::string name,
false);
sink.m_xp.push_tag_with_class ("span", "location", true);
sink.m_xp.add_text (text.get ());
- sink.m_xp.pop_tag (); // span
+ sink.m_xp.pop_tag ("span");
}
namespace {
bool is_label_text)
{
if (m_sink.get_highlight_color_for_range_idx (range_idx))
- m_sink.m_xp.pop_tag ();
+ m_sink.m_xp.pop_tag ("span");
if (is_label_text && m_sink.m_html_label_writer)
m_sink.m_html_label_writer->end_label ();
void
layout_printer<Sink>::print_trailing_fixits (linenum_type row)
{
+ typename Sink::auto_check_tag_nesting sentinel (m_sink);
+
/* Build a list of correction instances for the line,
potentially consolidating hints (for the sake of readability). */
line_corrections corrections (m_layout.m_file_cache, m_layout.m_char_policy,
}
}
- if (!corrections.m_corrections.is_empty ())
- m_sink.pop_html_tag ("td");
-
/* Add a trailing newline, if necessary. */
move_to_column (&column, 1 + m_layout.m_x_offset_display, false);
}
void
layout_printer<Sink>::print_line (linenum_type row)
{
+ typename Sink::auto_check_tag_nesting sentinel (m_sink);
+
char_span line
= m_layout.m_file_cache.get_source_line (m_layout.m_exploc.file, row);
if (!line)
to_html sink (xp, &richloc, label_writer);
layout_printer<to_html> lp (sink, layout,
diagnostic_kind == DK_DIAGNOSTIC_PATH);
+ xml::auto_check_tag_nesting sentinel (xp);
lp.print (*this);
}
void
layout_printer<Sink>::print (const diagnostic_source_print_policy &source_policy)
{
+ typename Sink::auto_check_tag_nesting sentinel (m_sink);
+
m_sink.push_html_tag_with_class ("table", "locus", false);
if (get_options ().show_ruler_p)
assert_class(tds[1], 'annotation')
assert_highlighted_text(tds[1][0], 'highlight-a', 'int')
assert_highlighted_text(tds[1][1], 'highlight-b', 'const char *')
-
-# For reference, here's the generated HTML:
-"""
- <span class="gcc-message" id="gcc-diag-0-message">format '<span class="gcc-quoted-text"><span class="high
-light-a">%i</span></span>' expects argument of type '<span class="gcc-quoted-text"><span class="highlight-a"
->int</span></span>', but argument 2 has type '<span class="gcc-quoted-text"><span class="highlight-b">const
-char *</span></span>'</span>
-
- <span class="gcc-option">[<a href="https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat">-Wfo
-rmat=</a>]</span>
- <table class="locus">
- <tbody class="line-span">
- <tr><td class="left-margin"> </td><td class="source"> printf("hello <span class="highlight-a">%i</span>", <span class="highlight-b">msg</span>); /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */</td></tr>
- <tr><td class="left-margin"> </td><td class="annotation"> <span class="highlight-a">~^</spa
-n> <span class="highlight-b">~~~</span></td></tr>
- <tr><td class="left-margin"> </td><td class="annotation"> <span class="highlight-a">|</spa
-n> <span class="highlight-b">|</span></td></tr>
- <tr><td class="left-margin"> </td><td class="annotation"> <span class="highlight-a">int</s
-pan> <span class="highlight-b">const char *</span></td></tr>
- <tr><td class="left-margin"> </td><td class="annotation"> %s</td></tr>
- </tbody>
- </table>
-"""
class printer
{
public:
- printer (element &insertion_point);
+ printer (element &insertion_point, bool check_popped_tags = true);
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 pop_tag (const char *expected_name);
void set_attr (const char *name, std::string value);
element *get_insertion_point () const;
+ size_t get_num_open_tags () const { return m_open_tags.size (); }
+
+ void DEBUG_FUNCTION dump () const;
+
private:
// borrowed ptrs:
std::vector<element *> m_open_tags;
+ bool m_check_popped_tags;
+};
+
+/* RAII class for ensuring that the tags nested correctly.
+ Verify that within an instance's lifetime that any pushes
+ to the printer's insertion point have been popped by the end. */
+
+class auto_check_tag_nesting
+{
+public:
+ auto_check_tag_nesting (const printer &xp)
+ : m_xp (xp),
+ m_initial_insertion_element (xp.get_insertion_point ())
+ {
+ }
+ ~auto_check_tag_nesting ()
+ {
+ /* Verify that we didn't pop too many tags within the printer,
+ or leave any tags open. */
+ gcc_assert (m_initial_insertion_element == m_xp.get_insertion_point ());
+ }
+
+private:
+ const printer &m_xp;
+ const element *m_initial_insertion_element;
};
// RAII for push/pop element on xml::printer
auto_print_element (printer &printer,
std::string name,
bool preserve_whitespace = false)
- : m_printer (printer)
+ : m_printer (printer),
+ m_name (std::move (name))
{
- m_printer.push_tag (name, preserve_whitespace);
+ m_printer.push_tag (m_name, preserve_whitespace);
}
~auto_print_element ()
{
- m_printer.pop_tag ();
+ m_printer.pop_tag (m_name.c_str ());
}
private:
printer &m_printer;
+ std::string m_name;
};
} // namespace xml
pp_string (pp, m_xml_src.c_str ());
}
-#if __GNUC__ >= 10
-# pragma GCC diagnostic pop
-#endif
-
// class printer
-printer::printer (element &insertion_point)
+printer::printer (element &insertion_point,
+ bool check_popped_tags)
+: m_check_popped_tags (check_popped_tags)
{
m_open_tags.push_back (&insertion_point);
}
push_element (std::move (new_element));
}
+/* Pop the current topmost tag.
+ If m_check_popped_tags, assert that the tag we're popping is
+ EXPECTED_NAME. */
+
void
-printer::pop_tag ()
+printer::pop_tag (const char *expected_name ATTRIBUTE_UNUSED)
{
+ gcc_assert (!m_open_tags.empty ());
+ if (m_check_popped_tags)
+ gcc_assert (expected_name == get_insertion_point ()->m_kind);
m_open_tags.pop_back ();
}
return m_open_tags.back ();
}
+void
+printer::dump () const
+{
+ pretty_printer pp;
+ pp.set_output_stream (stderr);
+ pp_printf (&pp, "open tags: %i:", (int)m_open_tags.size ());
+ for (auto iter : m_open_tags)
+ pp_printf (&pp, " <%s>", iter->m_kind.c_str ());
+ pp_newline (&pp);
+ pp_printf (&pp, "xml:");
+ pp_newline (&pp);
+ m_open_tags[0]->write_as_xml (&pp, 1, true);
+ pp_flush (&pp);
+}
+
+#if __GNUC__ >= 10
+# pragma GCC diagnostic pop
+#endif
+
} // namespace xml
#if CHECKING_P
xp.set_attr ("color", "red");
xp.add_text ("world");
xp.push_tag ("baz");
- xp.pop_tag ();
- xp.pop_tag ();
- xp.pop_tag ();
+ xp.pop_tag ("baz");
+ xp.pop_tag ("bar");
+ xp.pop_tag ("foo");
pretty_printer pp;
top.write_as_xml (&pp, 0, true);
xp.set_attr ("hastings", "1066");
xp.set_attr ("edgehill", "1642");
xp.set_attr ("naseby", "1645");
- xp.pop_tag ();
+ xp.pop_tag ("chronological");
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 ();
+ xp.pop_tag ("alphabetical");
pretty_printer pp;
top.write_as_xml (&pp, 0, true);