diff options
| author | Michael Howell <michael@notriddle.com> | 2024-03-29 12:31:34 -0700 |
|---|---|---|
| committer | Michael Howell <michael@notriddle.com> | 2024-03-29 13:31:35 -0700 |
| commit | 98642da6a9dda39e711b7f3520b0b6d40c1cd043 (patch) | |
| tree | bc3834326dc016ab7bb22636218b753f03c8aaf1 /compiler/rustc_resolve/src/rustdoc.rs | |
| parent | d74804636fa57e80d1e213fa9d2d65b27216b515 (diff) | |
| download | rust-98642da6a9dda39e711b7f3520b0b6d40c1cd043.tar.gz rust-98642da6a9dda39e711b7f3520b0b6d40c1cd043.zip | |
rustdoc: point at span in `include_str!`-ed md file
Diffstat (limited to 'compiler/rustc_resolve/src/rustdoc.rs')
| -rw-r--r-- | compiler/rustc_resolve/src/rustdoc.rs | 45 |
1 files changed, 38 insertions, 7 deletions
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 0ebcad3cdb8..0bc7579918c 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -194,12 +194,12 @@ pub fn attrs_to_doc_fragments<'a>( for (attr, item_id) in attrs { if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { let doc = beautify_doc_string(doc_str, comment_kind); - let kind = if attr.is_doc_comment() { - DocFragmentKind::SugaredDoc + let (span, kind) = if attr.is_doc_comment() { + (attr.span, DocFragmentKind::SugaredDoc) } else { - DocFragmentKind::RawDoc + (span_for_value(attr), DocFragmentKind::RawDoc) }; - let fragment = DocFragment { span: attr.span, doc, kind, item_id, indent: 0 }; + let fragment = DocFragment { span, doc, kind, item_id, indent: 0 }; doc_fragments.push(fragment); } else if !doc_only { other_attrs.push(attr.clone()); @@ -211,6 +211,16 @@ pub fn attrs_to_doc_fragments<'a>( (doc_fragments, other_attrs) } +fn span_for_value(attr: &ast::Attribute) -> Span { + if let ast::AttrKind::Normal(normal) = &attr.kind + && let ast::AttrArgs::Eq(_, ast::AttrArgsEq::Hir(meta)) = &normal.item.args + { + meta.span.with_ctxt(attr.span.ctxt()) + } else { + attr.span + } +} + /// Return the doc-comments on this item, grouped by the module they came from. /// The module can be different if this is a re-export with added documentation. /// @@ -482,15 +492,36 @@ pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> { /// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code. /// -/// This method will return `None` if we cannot construct a span from the source map or if the -/// fragments are not all sugared doc comments. It's difficult to calculate the correct span in -/// that case due to escaping and other source features. +/// This method does not always work, because markdown bytes don't necessarily match source bytes, +/// like if escapes are used in the string. In this case, it returns `None`. +/// +/// This method will return `Some` only if: +/// +/// - The doc is made entirely from sugared doc comments, which cannot contain escapes +/// - The doc is entirely from a single doc fragment, with a string literal, exactly equal +/// - The doc comes from `include_str!` pub fn source_span_for_markdown_range( tcx: TyCtxt<'_>, markdown: &str, md_range: &Range<usize>, fragments: &[DocFragment], ) -> Option<Span> { + if let &[fragment] = &fragments + && fragment.kind == DocFragmentKind::RawDoc + && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(fragment.span) + && snippet.trim_end() == markdown.trim_end() + && let Ok(md_range_lo) = u32::try_from(md_range.start) + && let Ok(md_range_hi) = u32::try_from(md_range.end) + { + // Single fragment with string that contains same bytes as doc. + return Some(Span::new( + fragment.span.lo() + rustc_span::BytePos(md_range_lo), + fragment.span.lo() + rustc_span::BytePos(md_range_hi), + fragment.span.ctxt(), + fragment.span.parent(), + )); + } + let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc); if !is_all_sugared_doc { |
