Handle unclosed HTML tags inside a markdown element

This fixes an issue where it was panicking due to an unbalanced HTML tag
when exiting a markdown element. The problem was that the tag stack was
left non-empty when processing was finished due to `end_tag` being out
of sync with the pulldown-cmark event tags.

There really should be better validation that the stack is in sync and
balanced, but this should address the main culprit of the interplay of
raw HTML tags and pulldown-cmark events.
This commit is contained in:
Eric Huss 2025-11-06 07:31:45 -08:00
parent 152132458e
commit 700839f77f
2 changed files with 31 additions and 12 deletions

View file

@ -583,8 +583,28 @@ where
} }
fn end_tag(&mut self, tag: TagEnd) { fn end_tag(&mut self, tag: TagEnd) {
// TODO: This should validate that the event stack is // TODO: This should validate that the event stack is properly
// properly synchronized with the tag stack. // synchronized with the tag stack. That, would likely require keeping
// a parallel "expected end tag" with the tag stack, since mapping a
// pulldown-cmark event tag to an HTML tag isn't always clear.
//
// Check for unclosed HTML tags when exiting a markdown event.
while let Some(node_id) = self.tag_stack.last() {
let node = self.tree.get(*node_id).unwrap().value();
let Node::Element(el) = node else {
break;
};
if !el.was_raw {
break;
}
warn!(
"unclosed HTML tag `<{}>` found in `{}` while exiting {tag:?}\n\
HTML tags must be closed before exiting a markdown element.",
el.name.local,
self.options.path.display(),
);
self.pop();
}
self.pop(); self.pop();
match tag { match tag {
TagEnd::TableHead => { TagEnd::TableHead => {

View file

@ -290,18 +290,17 @@ fn heading_with_unbalanced_html() {
BookTest::init(|_| {}) BookTest::init(|_| {})
.change_file("src/chapter_1.md", "### Option<T>") .change_file("src/chapter_1.md", "### Option<T>")
.run("build", |cmd| { .run("build", |cmd| {
cmd.expect_failure().expect_stderr(str![[r#" cmd.expect_stderr(str![[r#"
INFO Book building has started INFO Book building has started
INFO Running the html backend INFO Running the html backend
WARN unclosed HTML tag `<t>` found in `chapter_1.md` while exiting Heading(H3)
thread 'main' ([..]) panicked at crates/mdbook-html/src/html/tree.rs:[..] HTML tags must be closed before exiting a markdown element.
internal error: expected empty tag stack. INFO HTML book written to `[ROOT]/book`
path: `chapter_1.md`
element=Element { name: QualName { prefix: None, ns: Atom('http://www.w3.org/1999/xhtml' type=static), local: Atom('h3' type=inline) }, attrs: {}, self_closing: false, was_raw: false }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
"#]]); "#]]);
}); })
// .check_main_file("book/chapter_1.html", str![[""]]); .check_main_file(
"book/chapter_1.html",
str![[r##"<h3 id="option"><a class="header" href="#option">Option<t></t></a></h3>"##]],
);
} }