diff --git a/src/librustdoc/html/escape.rs b/src/librustdoc/html/escape.rs index ac9e2f42cc6d0..f94e76b1f021a 100644 --- a/src/librustdoc/html/escape.rs +++ b/src/librustdoc/html/escape.rs @@ -79,9 +79,12 @@ impl fmt::Display for EscapeBodyTextWithWbr<'_> { } else if (s.contains(':') && !next_is_colon()) || (s.contains('_') && !next_is_underscore()) { - EscapeBodyText(&text[last..i + 1]).fmt(fmt)?; + // `i + s.len()`, not `i + 1`: a cluster containing `_` or `:` can still be + // several bytes long, because UAX#29 GB9b joins a `Prepend` character to the + // one that follows it. + EscapeBodyText(&text[last..i + s.len()]).fmt(fmt)?; fmt.write_str("")?; - last = i + 1; + last = i + s.len(); } } if last < text.len() { diff --git a/src/librustdoc/html/escape/tests.rs b/src/librustdoc/html/escape/tests.rs index de702e1606353..247a1e42d18eb 100644 --- a/src/librustdoc/html/escape/tests.rs +++ b/src/librustdoc/html/escape/tests.rs @@ -40,6 +40,10 @@ fn escape_body_text_with_wbr() { assert_eq!(&E("ṼẽçÑñéå").to_string(), "ṼẽçÑñéå"); assert_eq!(&E("V\u{0300}e\u{0300}c\u{0300}D\u{0300}e\u{0300}q\u{0300}u\u{0300}e\u{0300}u\u{0300}e\u{0300}").to_string(), "V\u{0300}e\u{0300}c\u{0300}D\u{0300}e\u{0300}q\u{0300}u\u{0300}e\u{0300}u\u{0300}e\u{0300}"); assert_eq!(&E("LPFNACCESSIBLEOBJECTFROMWINDOW").to_string(), "LPFNACCESSIBLEOBJECTFROMWINDOW"); + // clusters where the `_` or `:` is not the first byte (UAX#29 GB9b `Prepend`) + assert_eq!(&E("abc\u{0D4E}_defgh").to_string(), "abc\u{0D4E}_defgh"); + assert_eq!(&E("abc\u{0605}:defgh").to_string(), "abc\u{0605}:defgh"); + assert_eq!(&E("first_\u{0300}second").to_string(), "first_\u{0300}second"); } // property test #[test] diff --git a/tests/rustdoc-html/item-name-grapheme-cluster-wbr.rs b/tests/rustdoc-html/item-name-grapheme-cluster-wbr.rs new file mode 100644 index 0000000000000..5244f970389ca --- /dev/null +++ b/tests/rustdoc-html/item-name-grapheme-cluster-wbr.rs @@ -0,0 +1,8 @@ +// Regression test for #160231: `` insertion sliced mid-character on an item name whose +// grapheme cluster starts with a `Prepend` character and contains `_`. + +#![crate_name = "foo"] +#![allow(mixed_script_confusables, non_camel_case_types)] + +//@ hasraw foo/index.html 'abcൎ_defgh' +pub struct abcൎ_defgh;