Skip to content

Commit 700839f

Browse files
committed
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.
1 parent 1521324 commit 700839f

File tree

2 files changed

+31
-12
lines changed

2 files changed

+31
-12
lines changed

crates/mdbook-html/src/html/tree.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,8 +583,28 @@ where
583583
}
584584

585585
fn end_tag(&mut self, tag: TagEnd) {
586-
// TODO: This should validate that the event stack is
587-
// properly synchronized with the tag stack.
586+
// TODO: This should validate that the event stack is properly
587+
// synchronized with the tag stack. That, would likely require keeping
588+
// a parallel "expected end tag" with the tag stack, since mapping a
589+
// pulldown-cmark event tag to an HTML tag isn't always clear.
590+
//
591+
// Check for unclosed HTML tags when exiting a markdown event.
592+
while let Some(node_id) = self.tag_stack.last() {
593+
let node = self.tree.get(*node_id).unwrap().value();
594+
let Node::Element(el) = node else {
595+
break;
596+
};
597+
if !el.was_raw {
598+
break;
599+
}
600+
warn!(
601+
"unclosed HTML tag `<{}>` found in `{}` while exiting {tag:?}\n\
602+
HTML tags must be closed before exiting a markdown element.",
603+
el.name.local,
604+
self.options.path.display(),
605+
);
606+
self.pop();
607+
}
588608
self.pop();
589609
match tag {
590610
TagEnd::TableHead => {

tests/testsuite/rendering.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -290,18 +290,17 @@ fn heading_with_unbalanced_html() {
290290
BookTest::init(|_| {})
291291
.change_file("src/chapter_1.md", "### Option<T>")
292292
.run("build", |cmd| {
293-
cmd.expect_failure().expect_stderr(str![[r#"
293+
cmd.expect_stderr(str![[r#"
294294
INFO Book building has started
295295
INFO Running the html backend
296-
297-
thread 'main' ([..]) panicked at crates/mdbook-html/src/html/tree.rs:[..]
298-
internal error: expected empty tag stack.
299-
300-
path: `chapter_1.md`
301-
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 }
302-
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
296+
WARN unclosed HTML tag `<t>` found in `chapter_1.md` while exiting Heading(H3)
297+
HTML tags must be closed before exiting a markdown element.
298+
INFO HTML book written to `[ROOT]/book`
303299
304300
"#]]);
305-
});
306-
// .check_main_file("book/chapter_1.html", str![[""]]);
301+
})
302+
.check_main_file(
303+
"book/chapter_1.html",
304+
str![[r##"<h3 id="option"><a class="header" href="#option">Option<t></t></a></h3>"##]],
305+
);
307306
}

0 commit comments

Comments
 (0)