diff --git a/Cargo.lock b/Cargo.lock index 4605b4ef4adec..98ced55604495 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -643,7 +643,7 @@ dependencies = [ "declare_clippy_lint", "filetime", "itertools", - "pulldown-cmark", + "pulldown-cmark 0.11.3", "regex", "rustc_tools_util 0.4.2", "serde", @@ -1275,6 +1275,12 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921" +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -2392,6 +2398,33 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "math-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2226363d63d9c12367ca74c261a198ba979f98b4311fded39239a9a904835e40" +dependencies = [ + "math-core-renderer-internal", + "memchr", + "phf 0.13.1", + "rustc-hash 2.1.1", + "strum 0.27.2", + "strum_macros 0.27.2", +] + +[[package]] +name = "math-core-renderer-internal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd17e13ea5087c4df7aeccf184eff5352546913e72cac8f4a64841980733913c" +dependencies = [ + "bitflags", + "dtoa", + "stable-arena", + "strum 0.27.2", + "strum_macros 0.27.2", +] + [[package]] name = "md-5" version = "0.10.6" @@ -2915,13 +2948,24 @@ dependencies = [ "phf_shared 0.12.1", ] +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared 0.13.1", + "serde", +] + [[package]] name = "phf_codegen" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator", + "phf_generator 0.11.3", "phf_shared 0.11.3", ] @@ -2935,6 +2979,29 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "phf_shared" version = "0.11.3" @@ -2953,6 +3020,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -3089,6 +3165,18 @@ dependencies = [ "unicase", ] +[[package]] +name = "pulldown-cmark" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" +dependencies = [ + "bitflags", + "memchr", + "pulldown-cmark-escape", + "unicase", +] + [[package]] name = "pulldown-cmark-escape" version = "0.11.0" @@ -4591,7 +4679,7 @@ version = "0.0.0" dependencies = [ "indexmap", "itertools", - "pulldown-cmark", + "pulldown-cmark 0.13.0", "rustc_arena", "rustc_ast", "rustc_ast_pretty", @@ -4883,6 +4971,7 @@ dependencies = [ "expect-test", "indexmap", "itertools", + "math-core", "minifier", "proc-macro2", "pulldown-cmark-escape", @@ -5314,12 +5403,18 @@ dependencies = [ "nom", "serde", "spdx-expression", - "strum", - "strum_macros", + "strum 0.24.1", + "strum_macros 0.24.3", "thiserror 1.0.69", "uuid", ] +[[package]] +name = "stable-arena" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a6572b16faf73db61fab0b1d848ce108e20d026dc20dcb043a3f83c73ad1c09" + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -5364,7 +5459,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ - "phf_generator", + "phf_generator 0.11.3", "phf_shared 0.11.3", "proc-macro2", "quote", @@ -5391,6 +5486,12 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" + [[package]] name = "strum_macros" version = "0.24.3" @@ -5404,6 +5505,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index dd15e879c6440..11e9b054f9a7e 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" # tidy-alphabetical-start indexmap = "2.4.0" itertools = "0.12" -pulldown-cmark = { version = "0.11", features = ["html"], default-features = false } +pulldown-cmark = { version = "0.13", features = ["html"], default-features = false } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 9f74a7801d2ee..a107c3cd3bf74 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -254,6 +254,7 @@ pub fn main_body_opts() -> Options { | Options::ENABLE_STRIKETHROUGH | Options::ENABLE_TASKLISTS | Options::ENABLE_SMART_PUNCTUATION + | Options::ENABLE_MATH } fn strip_generics_from_path_segment(segment: Vec) -> Result { @@ -403,7 +404,7 @@ pub fn may_be_doc_link(link_type: LinkType) -> bool { | LinkType::CollapsedUnknown | LinkType::Shortcut | LinkType::ShortcutUnknown => true, - LinkType::Autolink | LinkType::Email => false, + LinkType::Autolink | LinkType::Email | LinkType::WikiLink { .. } => false, } } diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index f1bf6e399fb1a..e4152496ae50e 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -18,6 +18,7 @@ pub static CRATES: &[&str] = &[ "derive_builder_core", "digest", "equivalent", + "fastrand", "fluent-bundle", "fluent-langneg", "fluent-syntax", @@ -37,6 +38,8 @@ pub static CRATES: &[&str] = &[ "pest", "pest_generator", "pest_meta", + "phf_generator", + "phf_shared", "proc-macro2", "quote", "rustc-hash", @@ -45,6 +48,7 @@ pub static CRATES: &[&str] = &[ "serde_core", "serde_derive_internals", "sha2", + "siphasher", "smallvec", "stable_deref_trait", "strsim", diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 033d3ecdd03de..0b9ec1ee671f4 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -14,6 +14,7 @@ askama = { version = "0.15.4", default-features = false, features = ["alloc", "c base64 = "0.21.7" indexmap = { version = "2", features = ["serde"] } itertools = "0.12" +math-core = "0.2.2" minifier = { version = "0.3.5", default-features = false } proc-macro2 = "1.0.103" pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index c472c20a7dc71..f9d6956c102ab 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -36,6 +36,7 @@ use std::str::{self, CharIndices}; use std::sync::atomic::AtomicUsize; use std::sync::{Arc, Weak}; +use math_core::{LatexToMathML, MathCoreConfig, MathDisplay}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Diag, DiagMessage}; use rustc_hir::def_id::LocalDefId; @@ -71,6 +72,7 @@ pub(crate) fn summary_opts() -> Options { | Options::ENABLE_STRIKETHROUGH | Options::ENABLE_TASKLISTS | Options::ENABLE_SMART_PUNCTUATION + | Options::ENABLE_MATH } #[derive(Debug, Clone, Copy)] @@ -500,6 +502,51 @@ impl<'a, I: Iterator>> Iterator for SpannedLinkReplacer< } } +/// Convert $\LaTeX$ math markup into MathML Core +struct Math<'a, I: Iterator>> { + inner: I, + converter: LatexToMathML, +} + +impl<'a, I: Iterator>> Math<'a, I> { + fn new(iter: I) -> Self { + Self { + inner: iter, + converter: LatexToMathML::new(MathCoreConfig { + pretty_print: math_core::PrettyPrint::Never, + xml_namespace: false, + ..MathCoreConfig::default() + }) + .expect("no custom macros are specified, so error should be impossible"), + } + } +} + +impl<'a, I: Iterator>> Iterator for Math<'a, I> { + type Item = Event<'a>; + + fn next(&mut self) -> Option { + match self.inner.next() { + Some(Event::InlineMath(latex)) => { + let mathml = self + .converter + .convert_with_local_counter(&latex, MathDisplay::Inline) + .expect("a production-ready version of this should handle the error somehow"); + Some(Event::InlineHtml(mathml.into())) + } + Some(Event::DisplayMath(latex)) => { + let mathml = self + .converter + .convert_with_local_counter(&latex, MathDisplay::Block) + .expect("a production-ready version of this should handle the error somehow"); + // as counterintuitive as it might seem, block equations are still parsed as inline + Some(Event::InlineHtml(mathml.into())) + } + event => event, + } + } +} + /// Wrap HTML tables into `
` to prevent having the doc blocks width being too big. struct TableWrapper<'a, I: Iterator>> { inner: I, @@ -624,7 +671,7 @@ fn check_if_allowed_tag(t: &TagEnd) -> bool { | TagEnd::Strong | TagEnd::Strikethrough | TagEnd::Link - | TagEnd::BlockQuote + | TagEnd::BlockQuote(..) ) } @@ -1369,6 +1416,7 @@ impl<'a> Markdown<'a> { let p = SpannedLinkReplacer::new(p, links); let p = footnotes::Footnotes::new(p, existing_footnotes); let p = TableWrapper::new(p.map(|(ev, _)| ev)); + let p = Math::new(p); CodeBlocks::new(p, codes, edition, playground) }) } @@ -1451,6 +1499,7 @@ impl MarkdownWithToc<'_> { let p = footnotes::Footnotes::new(p, existing_footnotes); let p = TableWrapper::new(p.map(|(ev, _)| ev)); let p = CodeBlocks::new(p, codes, edition, playground); + let p = Math::new(p); html::push_html(&mut s, p); }); @@ -1500,6 +1549,7 @@ impl<'a> MarkdownItemInfo<'a> { let p = p.filter(|event| { !matches!(event, Event::Start(Tag::Paragraph) | Event::End(TagEnd::Paragraph)) }); + let p = Math::new(p); html::write_html_fmt(&mut f, p) }) } @@ -1914,7 +1964,9 @@ pub(crate) fn markdown_links<'md, R>( span_for_link(&dest_url, span) } } - LinkType::Autolink | LinkType::Email => unreachable!(), + LinkType::Autolink | LinkType::Email | LinkType::WikiLink { .. } => { + unreachable!() + } }; if let Some(link) = preprocess_link(MarkdownLink { diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index ecf7acbd7ce6e..82cd8aaa1471e 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1123,7 +1123,7 @@ fn check_doc<'a, Events: Iterator, Range next_range.end, + End(TagEnd::BlockQuote(_)) => next_range.end, _ => next_range.start, }; if let Some(refdefrange) = looks_like_refdef(doc, range.start..next_start) && @@ -1147,7 +1147,7 @@ fn check_doc<'a, Events: Iterator, Range { + End(TagEnd::BlockQuote(_)) => { blockquote_level -= 1; containers.pop(); }, diff --git a/tests/rustdoc-html/latex-math.rs b/tests/rustdoc-html/latex-math.rs new file mode 100644 index 0000000000000..918dd2923766c --- /dev/null +++ b/tests/rustdoc-html/latex-math.rs @@ -0,0 +1,14 @@ +#![crate_name = "foo"] + +//@ hasraw foo/bar/index.html '' +/// test math $\frac{ d }{ d x } \tan x = \frac{ 1 }{ \cos^2 x }$ +pub mod bar {} + +//@ hasraw foo/baz/index.html '' +/// test math $$\frac{ d }{ d x } \tan x = \frac{ 1 }{ \cos^2 x }$$ +pub mod baz {} + +//@ !hasraw foo/frob/index.html '' +//@ !hasraw foo/frob/index.html '' +/// not math $\frac{ d } +pub mod frob {}