From 9f6b4255c20bbfdb28e963e4d4ef4c8813cb911e Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Fri, 28 Mar 2025 23:50:26 +0900 Subject: [PATCH] fix: resolve doc path if outer comments exist on module and replace from cfg_attr bit to doc_place bit Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> --- crates/hir-expand/src/attrs.rs | 47 +++++++-------- crates/hir/src/attrs.rs | 17 +++++- crates/ide/src/doc_links/tests.rs | 57 +++++++++++++++++++ .../highlight_module_docs_inline.html | 4 +- crates/ide/src/syntax_highlighting/tests.rs | 8 +-- 5 files changed, 99 insertions(+), 34 deletions(-) diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index bb17eb0627..b350a6f8ac 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -129,9 +129,8 @@ impl RawAttrs { .cloned() .chain(b.slice.iter().map(|it| { let mut it = it.clone(); - it.id.id = (it.id.ast_index() as u32 + last_ast_index) - | ((it.id.cfg_attr_index().unwrap_or(0) as u32) - << AttrId::AST_INDEX_BITS); + let id = it.id.ast_index() as u32 + last_ast_index; + it.id = AttrId::new(id as usize, it.id.is_inner_attr()); it })) .collect::>(); @@ -175,25 +174,21 @@ pub struct AttrId { // FIXME: This only handles a single level of cfg_attr nesting // that is `#[cfg_attr(all(), cfg_attr(all(), cfg(any())))]` breaks again impl AttrId { - const CFG_ATTR_BITS: usize = 7; const AST_INDEX_MASK: usize = 0x00FF_FFFF; - const AST_INDEX_BITS: usize = Self::AST_INDEX_MASK.count_ones() as usize; - const CFG_ATTR_SET_BITS: u32 = 1 << 31; + const INNER_ATTR_BIT: usize = 1 << 31; + + pub fn new(id: usize, is_inner: bool) -> Self { + let id = id & Self::AST_INDEX_MASK; + let id = if is_inner { id | Self::INNER_ATTR_BIT } else { id }; + Self { id: id as u32 } + } pub fn ast_index(&self) -> usize { self.id as usize & Self::AST_INDEX_MASK } - pub fn cfg_attr_index(&self) -> Option { - if self.id & Self::CFG_ATTR_SET_BITS == 0 { - None - } else { - Some(self.id as usize >> Self::AST_INDEX_BITS) - } - } - - pub fn with_cfg_attr(self, idx: usize) -> AttrId { - AttrId { id: self.id | ((idx as u32) << Self::AST_INDEX_BITS) | Self::CFG_ATTR_SET_BITS } + pub fn is_inner_attr(&self) -> bool { + (self.id as usize) & Self::INNER_ATTR_BIT != 0 } } @@ -333,10 +328,7 @@ impl Attr { None => return smallvec![self.clone()], }; let index = self.id; - let attrs = parts - .enumerate() - .take(1 << AttrId::CFG_ATTR_BITS) - .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx))); + let attrs = parts.filter_map(|attr| Attr::from_tt(db, attr, index)); let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg); let cfg = CfgExpr::parse(&cfg); @@ -467,13 +459,18 @@ fn unescape(s: &str) -> Option> { pub fn collect_attrs( owner: &dyn ast::HasAttrs, ) -> impl Iterator)> { - let inner_attrs = inner_attributes(owner.syntax()).into_iter().flatten(); - let outer_attrs = - ast::AttrDocCommentIter::from_syntax_node(owner.syntax()).filter(|el| match el { + let inner_attrs = + inner_attributes(owner.syntax()).into_iter().flatten().map(|attr| (attr, true)); + let outer_attrs = ast::AttrDocCommentIter::from_syntax_node(owner.syntax()) + .filter(|el| match el { Either::Left(attr) => attr.kind().is_outer(), Either::Right(comment) => comment.is_outer(), - }); - outer_attrs.chain(inner_attrs).enumerate().map(|(id, attr)| (AttrId { id: id as u32 }, attr)) + }) + .map(|attr| (attr, false)); + outer_attrs + .chain(inner_attrs) + .enumerate() + .map(|(id, (attr, is_inner))| (AttrId::new(id, is_inner), attr)) } fn inner_attributes( diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 2d1727a6e9..38ba8d6a31 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -105,11 +105,13 @@ impl HasAttrs for crate::Crate { /// Resolves the item `link` points to in the scope of `def`. pub fn resolve_doc_path_on( db: &dyn HirDatabase, - def: impl HasAttrs, + def: impl HasAttrs + Copy, link: &str, ns: Option, ) -> Option { - resolve_doc_path_on_(db, link, def.attr_id(), ns) + let is_inner = + def.attrs(db).by_key(&intern::sym::doc).attrs().all(|attr| attr.id.is_inner_attr()); + resolve_doc_path_on_(db, link, def.attr_id(), ns, is_inner) } fn resolve_doc_path_on_( @@ -117,9 +119,18 @@ fn resolve_doc_path_on_( link: &str, attr_id: AttrDefId, ns: Option, + is_inner: bool, ) -> Option { let resolver = match attr_id { - AttrDefId::ModuleId(it) => it.resolver(db), + AttrDefId::ModuleId(it) => { + if is_inner { + it.resolver(db) + } else if let Some(parent) = Module::from(it).parent(db) { + parent.id.resolver(db) + } else { + it.resolver(db) + } + } AttrDefId::FieldId(it) => it.parent.resolver(db), AttrDefId::AdtId(it) => it.resolver(db), AttrDefId::FunctionId(it) => it.resolver(db), diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs index 91785be8d8..6d56b98e57 100644 --- a/crates/ide/src/doc_links/tests.rs +++ b/crates/ide/src/doc_links/tests.rs @@ -575,6 +575,40 @@ struct S$0(i32); ); } +#[test] +fn doc_links_module() { + check_doc_links( + r#" +/// [`M`] +/// [`M::f`] +mod M$0 { + //^ M + #![doc = "inner_item[`M::S`]"] + + pub fn f() {} + //^ M::f + pub struct S; + //^ M::S +} +"#, + ); + + check_doc_links( + r#" +mod M$0 { + //^ super::M + //! [`super::M`] + //! [`super::M::f`] + //! [`super::M::S`] + pub fn f() {} + //^ super::M::f + pub struct S; + //^ super::M::S +} +"#, + ); +} + #[test] fn rewrite_html_root_url() { check_rewrite( @@ -690,6 +724,29 @@ fn rewrite_intra_doc_link_with_anchor() { ); } +#[test] +fn rewrite_module() { + check_rewrite( + r#" +//- /main.rs crate:foo +/// [Foo] +pub mod $0Foo{ +}; +"#, + expect![[r#"[Foo](https://docs.rs/foo/*/foo/Foo/index.html)"#]], + ); + + check_rewrite( + r#" +//- /main.rs crate:foo +pub mod $0Foo{ + //! [super::Foo] +}; +"#, + expect![[r#"[super::Foo](https://docs.rs/foo/*/foo/Foo/index.html)"#]], + ); +} + #[test] fn rewrite_intra_doc_link_to_associated_item() { check_rewrite( diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html index 9996a87158..75d96b422c 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html @@ -40,9 +40,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
//! [Struct]
+
//! [foo::Struct]
 //! This is an intra doc injection test for modules
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index dd359326c6..a8d9530946 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -1072,9 +1072,9 @@ fn test_mod_hl_injection() {
     check_highlighting(
         r##"
 //- /foo.rs
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;
@@ -1097,9 +1097,9 @@ mod foo;
 /// This is an intra doc injection test for modules
 mod foo;
 //- /foo.rs
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
-//! [Struct]
+//! [foo::Struct]
 //! This is an intra doc injection test for modules
 
 pub struct Struct;