Skip to content

Commit 83000f5

Browse files
committed
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>
1 parent aded72f commit 83000f5

File tree

5 files changed

+129
-63
lines changed

5 files changed

+129
-63
lines changed

crates/hir-expand/src/attrs.rs

+52-54
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,8 @@ impl RawAttrs {
106106
.cloned()
107107
.chain(b.slice.iter().map(|it| {
108108
let mut it = it.clone();
109-
it.id.id = (it.id.ast_index() as u32 + last_ast_index)
110-
| ((it.id.cfg_attr_index().unwrap_or(0) as u32)
111-
<< AttrId::AST_INDEX_BITS);
109+
let id = it.id.ast_index() as u32 + last_ast_index;
110+
it.id = AttrId::new(id as usize, it.id.is_inner_attr());
112111
it
113112
}))
114113
.collect::<Vec<_>>();
@@ -128,40 +127,38 @@ impl RawAttrs {
128127
}
129128

130129
let cfg_options = krate.cfg_options(db);
131-
let new_attrs =
132-
self.iter()
133-
.flat_map(|attr| -> SmallVec<[_; 1]> {
134-
let is_cfg_attr =
135-
attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr.clone());
136-
if !is_cfg_attr {
137-
return smallvec![attr.clone()];
138-
}
130+
let new_attrs = self
131+
.iter()
132+
.flat_map(|attr| -> SmallVec<[_; 1]> {
133+
let is_cfg_attr =
134+
attr.path.as_ident().is_some_and(|name| *name == sym::cfg_attr.clone());
135+
if !is_cfg_attr {
136+
return smallvec![attr.clone()];
137+
}
139138

140-
let subtree = match attr.token_tree_value() {
141-
Some(it) => it,
142-
_ => return smallvec![attr.clone()],
143-
};
144-
145-
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
146-
Some(it) => it,
147-
None => return smallvec![attr.clone()],
148-
};
149-
let index = attr.id;
150-
let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
151-
|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)),
152-
);
153-
154-
let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
155-
let cfg = CfgExpr::parse(&cfg);
156-
if cfg_options.check(&cfg) == Some(false) {
157-
smallvec![]
158-
} else {
159-
cov_mark::hit!(cfg_attr_active);
160-
161-
attrs.collect()
162-
}
163-
})
164-
.collect::<Vec<_>>();
139+
let subtree = match attr.token_tree_value() {
140+
Some(it) => it,
141+
_ => return smallvec![attr.clone()],
142+
};
143+
144+
let (cfg, parts) = match parse_cfg_attr_input(subtree) {
145+
Some(it) => it,
146+
None => return smallvec![attr.clone()],
147+
};
148+
let index = attr.id;
149+
let attrs = parts.filter_map(|attr| Attr::from_tt(db, attr, index));
150+
151+
let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
152+
let cfg = CfgExpr::parse(&cfg);
153+
if cfg_options.check(&cfg) == Some(false) {
154+
smallvec![]
155+
} else {
156+
cov_mark::hit!(cfg_attr_active);
157+
158+
attrs.collect()
159+
}
160+
})
161+
.collect::<Vec<_>>();
165162
let entries = if new_attrs.is_empty() {
166163
None
167164
} else {
@@ -183,25 +180,21 @@ pub struct AttrId {
183180
// FIXME: This only handles a single level of cfg_attr nesting
184181
// that is `#[cfg_attr(all(), cfg_attr(all(), cfg(any())))]` breaks again
185182
impl AttrId {
186-
const CFG_ATTR_BITS: usize = 7;
187183
const AST_INDEX_MASK: usize = 0x00FF_FFFF;
188-
const AST_INDEX_BITS: usize = Self::AST_INDEX_MASK.count_ones() as usize;
189-
const CFG_ATTR_SET_BITS: u32 = 1 << 31;
184+
const INNER_ATTR_BIT: usize = 1 << 31;
190185

191-
pub fn ast_index(&self) -> usize {
192-
self.id as usize & Self::AST_INDEX_MASK
186+
pub fn new(id: usize, is_inner: bool) -> Self {
187+
let id = id & Self::AST_INDEX_MASK;
188+
let id = if is_inner { id | Self::INNER_ATTR_BIT } else { id };
189+
Self { id: id as u32 }
193190
}
194191

195-
pub fn cfg_attr_index(&self) -> Option<usize> {
196-
if self.id & Self::CFG_ATTR_SET_BITS == 0 {
197-
None
198-
} else {
199-
Some(self.id as usize >> Self::AST_INDEX_BITS)
200-
}
192+
pub fn ast_index(&self) -> usize {
193+
self.id as usize & Self::AST_INDEX_MASK
201194
}
202195

203-
pub fn with_cfg_attr(self, idx: usize) -> AttrId {
204-
AttrId { id: self.id | ((idx as u32) << Self::AST_INDEX_BITS) | Self::CFG_ATTR_SET_BITS }
196+
pub fn is_inner_attr(&self) -> bool {
197+
(self.id as usize) & Self::INNER_ATTR_BIT != 0
205198
}
206199
}
207200

@@ -439,13 +432,18 @@ fn unescape(s: &str) -> Option<Cow<'_, str>> {
439432
pub fn collect_attrs(
440433
owner: &dyn ast::HasAttrs,
441434
) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
442-
let inner_attrs = inner_attributes(owner.syntax()).into_iter().flatten();
443-
let outer_attrs =
444-
ast::AttrDocCommentIter::from_syntax_node(owner.syntax()).filter(|el| match el {
435+
let inner_attrs =
436+
inner_attributes(owner.syntax()).into_iter().flatten().map(|attr| (attr, true));
437+
let outer_attrs = ast::AttrDocCommentIter::from_syntax_node(owner.syntax())
438+
.filter(|el| match el {
445439
Either::Left(attr) => attr.kind().is_outer(),
446440
Either::Right(comment) => comment.is_outer(),
447-
});
448-
outer_attrs.chain(inner_attrs).enumerate().map(|(id, attr)| (AttrId { id: id as u32 }, attr))
441+
})
442+
.map(|attr| (attr, false));
443+
outer_attrs
444+
.chain(inner_attrs)
445+
.enumerate()
446+
.map(|(id, (attr, is_inner))| (AttrId::new(id, is_inner), attr))
449447
}
450448

451449
fn inner_attributes(

crates/hir/src/attrs.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,32 @@ impl HasAttrs for crate::Crate {
105105
/// Resolves the item `link` points to in the scope of `def`.
106106
pub fn resolve_doc_path_on(
107107
db: &dyn HirDatabase,
108-
def: impl HasAttrs,
108+
def: impl HasAttrs + Copy,
109109
link: &str,
110110
ns: Option<Namespace>,
111111
) -> Option<DocLinkDef> {
112-
resolve_doc_path_on_(db, link, def.attr_id(), ns)
112+
let is_inner =
113+
def.attrs(db).by_key(&intern::sym::doc).attrs().all(|attr| attr.id.is_inner_attr());
114+
resolve_doc_path_on_(db, link, def.attr_id(), ns, is_inner)
113115
}
114116

115117
fn resolve_doc_path_on_(
116118
db: &dyn HirDatabase,
117119
link: &str,
118120
attr_id: AttrDefId,
119121
ns: Option<Namespace>,
122+
is_inner: bool,
120123
) -> Option<DocLinkDef> {
121124
let resolver = match attr_id {
122-
AttrDefId::ModuleId(it) => it.resolver(db),
125+
AttrDefId::ModuleId(it) => {
126+
if is_inner {
127+
it.resolver(db)
128+
} else if let Some(parent) = Module::from(it).parent(db) {
129+
parent.id.resolver(db)
130+
} else {
131+
it.resolver(db)
132+
}
133+
}
123134
AttrDefId::FieldId(it) => it.parent.resolver(db),
124135
AttrDefId::AdtId(it) => it.resolver(db),
125136
AttrDefId::FunctionId(it) => it.resolver(db),

crates/ide/src/doc_links/tests.rs

+57
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,40 @@ struct S$0(i32);
575575
);
576576
}
577577

578+
#[test]
579+
fn doc_links_module() {
580+
check_doc_links(
581+
r#"
582+
/// [`M`]
583+
/// [`M::f`]
584+
mod M$0 {
585+
//^ M
586+
#![doc = "inner_item[`M::S`]"]
587+
588+
pub fn f() {}
589+
//^ M::f
590+
pub struct S;
591+
//^ M::S
592+
}
593+
"#,
594+
);
595+
596+
check_doc_links(
597+
r#"
598+
mod M$0 {
599+
//^ super::M
600+
//! [`super::M`]
601+
//! [`super::M::f`]
602+
//! [`super::M::S`]
603+
pub fn f() {}
604+
//^ super::M::f
605+
pub struct S;
606+
//^ super::M::S
607+
}
608+
"#,
609+
);
610+
}
611+
578612
#[test]
579613
fn rewrite_html_root_url() {
580614
check_rewrite(
@@ -690,6 +724,29 @@ fn rewrite_intra_doc_link_with_anchor() {
690724
);
691725
}
692726

727+
#[test]
728+
fn rewrite_module() {
729+
check_rewrite(
730+
r#"
731+
//- /main.rs crate:foo
732+
/// [Foo]
733+
pub mod $0Foo{
734+
};
735+
"#,
736+
expect![[r#"[Foo](https://docs.rs/foo/*/foo/Foo/index.html)"#]],
737+
);
738+
739+
check_rewrite(
740+
r#"
741+
//- /main.rs crate:foo
742+
pub mod $0Foo{
743+
//! [super::Foo]
744+
};
745+
"#,
746+
expect![[r#"[super::Foo](https://docs.rs/foo/*/foo/Foo/index.html)"#]],
747+
);
748+
}
749+
693750
#[test]
694751
fn rewrite_intra_doc_link_to_associated_item() {
695752
check_rewrite(

crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@
4040
.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
4141
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
4242
</style>
43-
<pre><code><span class="comment documentation">//! </span><span class="struct documentation injected intra_doc_link">[Struct]</span>
43+
<pre><code><span class="comment documentation">//! </span><span class="struct documentation injected intra_doc_link">[foo::Struct]</span>
4444
<span class="comment documentation">//! This is an intra doc injection test for modules</span>
45-
<span class="comment documentation">//! </span><span class="struct documentation injected intra_doc_link">[Struct]</span>
45+
<span class="comment documentation">//! </span><span class="struct documentation injected intra_doc_link">[foo::Struct]</span>
4646
<span class="comment documentation">//! This is an intra doc injection test for modules</span>
4747

4848
<span class="keyword">pub</span> <span class="keyword">struct</span> <span class="struct declaration public">Struct</span><span class="semicolon">;</span>

crates/ide/src/syntax_highlighting/tests.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1072,9 +1072,9 @@ fn test_mod_hl_injection() {
10721072
check_highlighting(
10731073
r##"
10741074
//- /foo.rs
1075-
//! [Struct]
1075+
//! [foo::Struct]
10761076
//! This is an intra doc injection test for modules
1077-
//! [Struct]
1077+
//! [foo::Struct]
10781078
//! This is an intra doc injection test for modules
10791079
10801080
pub struct Struct;
@@ -1097,9 +1097,9 @@ mod foo;
10971097
/// This is an intra doc injection test for modules
10981098
mod foo;
10991099
//- /foo.rs
1100-
//! [Struct]
1100+
//! [foo::Struct]
11011101
//! This is an intra doc injection test for modules
1102-
//! [Struct]
1102+
//! [foo::Struct]
11031103
//! This is an intra doc injection test for modules
11041104
11051105
pub struct Struct;

0 commit comments

Comments
 (0)