From 22065ebc79efbf5b0d6ffe36673139aafcd90db6 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 5 Nov 2025 11:42:43 -0800 Subject: [PATCH] Give a warning for unclosed HTML tags This changes the internal error message to a warning to let the user know that the HTML tags are unbalanced. In the future this will be a denyable lint. This is a very primitive approach of just ignoring the end tag. Ideally it should recover using the standard HTML parsing algorithm, since there is a chance that there will be a cascade of errors under certain unbalanced situations. --- crates/mdbook-html/src/html/tree.rs | 27 +++++++++++++++------------ tests/testsuite/rendering.rs | 12 +++--------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/crates/mdbook-html/src/html/tree.rs b/crates/mdbook-html/src/html/tree.rs index b732ac44..4e1d8f35 100644 --- a/crates/mdbook-html/src/html/tree.rs +++ b/crates/mdbook-html/src/html/tree.rs @@ -19,7 +19,7 @@ use pulldown_cmark::{Alignment, CodeBlockKind, CowStr, Event, LinkType, Tag, Tag use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::ops::Deref; -use tracing::{error, trace, warn}; +use tracing::{trace, warn}; /// Helper to create a [`QualName`]. macro_rules! attr_qual_name { @@ -664,9 +664,18 @@ where *is_raw = false; if self.is_html_tag_matching(&tag.name) { self.pop(); + } else { + // The proper thing to do here is to recover. However, the HTML + // parsing algorithm for that is quite complex. See + // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody + // and the adoption agency algorithm. + warn!( + "unexpected HTML end tag `` found in `{}`\n\ + Check that the HTML tags are properly balanced.", + tag.name, + self.options.path.display() + ); } - // else the stack is corrupt. I'm not really sure - // what to do here... } /// This is used to verify HTML parsing keeps the stack of tags in sync. @@ -675,16 +684,10 @@ where if let Node::Element(el) = current && el.name() == name { - return true; + true + } else { + false } - error!( - "internal error: HTML tag stack out of sync.\n - path: `{}`\n\ - current={current:?}\n\ - pop name: {name}", - self.options.path.display() - ); - false } /// Eats all pulldown-cmark events until the next `End` matching the diff --git a/tests/testsuite/rendering.rs b/tests/testsuite/rendering.rs index 13c157cf..e0e77039 100644 --- a/tests/testsuite/rendering.rs +++ b/tests/testsuite/rendering.rs @@ -274,18 +274,12 @@ fn unbalanced_html_tags() { cmd.expect_stderr(str![[r#" INFO Book building has started INFO Running the html backend -ERROR internal error: HTML tag stack out of sync. - - path: `chapter_1.md` -current=Element(Element { name: QualName { prefix: None, ns: Atom('http://www.w3.org/1999/xhtml' type=static), local: Atom('span' type=inline) }, attrs: {}, self_closing: false, was_raw: true }) -pop name: div + WARN unexpected HTML end tag `` found in `chapter_1.md` +Check that the HTML tags are properly balanced. WARN unclosed HTML tag `
` found in `chapter_1.md` INFO HTML book written to `[ROOT]/book` "#]]); }) - .check_main_file( - "book/chapter_1.html", - str!["
xfoo
"], - ); + .check_main_file("book/chapter_1.html", str!["
xfoo
"]); }