Skip to content

Commit de69c73

Browse files
committed
fix: resolve doc path if outer comments exist on module
Signed-off-by: Hayashi Mikihiro <[email protected]>
1 parent 0d64633 commit de69c73

File tree

5 files changed

+110
-17
lines changed

5 files changed

+110
-17
lines changed

crates/hir-expand/src/attrs.rs

+27-7
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,18 @@ impl RawAttrs {
5151
) -> Self {
5252
let entries: Vec<_> = collect_attrs(owner)
5353
.filter_map(|(id, attr)| match attr {
54-
Either::Left(attr) => {
55-
attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id))
56-
}
54+
Either::Left(attr) => Attr::from_src(db, attr, span_map, id),
5755
Either::Right(comment) => comment.doc_comment().map(|doc| {
5856
let span = span_map.span_for_range(comment.syntax().text_range());
5957
let (text, kind) =
6058
desugar_doc_comment_text(doc, DocCommentDesugarMode::ProcMacro);
6159
Attr {
6260
id,
61+
place: if comment.is_inner() {
62+
AttrPlacement::Inner
63+
} else {
64+
AttrPlacement::Outer
65+
},
6366
input: Some(Box::new(AttrInput::Literal(tt::Literal {
6467
symbol: text,
6568
span,
@@ -147,8 +150,9 @@ impl RawAttrs {
147150
None => return smallvec![attr.clone()],
148151
};
149152
let index = attr.id;
153+
let place = attr.place;
150154
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)),
155+
|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx), place),
152156
);
153157

154158
let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
@@ -211,6 +215,15 @@ pub struct Attr {
211215
pub path: Interned<ModPath>,
212216
pub input: Option<Box<AttrInput>>,
213217
pub ctxt: SyntaxContext,
218+
pub place: AttrPlacement,
219+
}
220+
221+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
222+
pub enum AttrPlacement {
223+
/// `#![attr]`
224+
Inner,
225+
/// `#[attr]`
226+
Outer,
214227
}
215228

216229
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -233,10 +246,16 @@ impl fmt::Display for AttrInput {
233246
impl Attr {
234247
fn from_src(
235248
db: &dyn ExpandDatabase,
236-
ast: ast::Meta,
249+
ast_attr: ast::Attr,
237250
span_map: SpanMapRef<'_>,
238251
id: AttrId,
239252
) -> Option<Attr> {
253+
let ast = ast_attr.meta()?;
254+
let place = if ast_attr.excl_token().is_some() {
255+
AttrPlacement::Inner
256+
} else {
257+
AttrPlacement::Outer
258+
};
240259
let path = ast.path()?;
241260
let range = path.syntax().text_range();
242261
let path = Interned::new(ModPath::from_src(db, path, &mut |range| {
@@ -257,13 +276,14 @@ impl Attr {
257276
} else {
258277
None
259278
};
260-
Some(Attr { id, path, input, ctxt: span.ctx })
279+
Some(Attr { id, path, input, ctxt: span.ctx, place })
261280
}
262281

263282
fn from_tt(
264283
db: &dyn ExpandDatabase,
265284
mut tt: tt::TokenTreesView<'_>,
266285
id: AttrId,
286+
place: AttrPlacement,
267287
) -> Option<Attr> {
268288
if matches!(tt.flat_tokens(),
269289
[tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym, .. })), ..]
@@ -314,7 +334,7 @@ impl Attr {
314334
}
315335
_ => None,
316336
};
317-
Some(Attr { id, path, input, ctxt })
337+
Some(Attr { id, path, input, ctxt, place })
318338
}
319339

320340
pub fn path(&self) -> &ModPath {

crates/hir/src/attrs.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use hir_def::{
1010
per_ns::Namespace,
1111
resolver::{HasResolver, Resolver, TypeNs},
1212
};
13-
use hir_expand::{mod_path::PathKind, name::Name};
13+
use hir_expand::{attrs::AttrPlacement, mod_path::PathKind, name::Name};
1414
use hir_ty::{db::HirDatabase, method_resolution};
1515

1616
use crate::{
@@ -102,21 +102,37 @@ impl HasAttrs for crate::Crate {
102102
/// Resolves the item `link` points to in the scope of `def`.
103103
pub fn resolve_doc_path_on(
104104
db: &dyn HirDatabase,
105-
def: impl HasAttrs,
105+
def: impl HasAttrs + Copy,
106106
link: &str,
107107
ns: Option<Namespace>,
108108
) -> Option<DocLinkDef> {
109-
resolve_doc_path_on_(db, link, def.attr_id(), ns)
109+
let is_outer = def
110+
.attrs(db)
111+
.by_key(&intern::sym::doc)
112+
.attrs()
113+
.any(|attr| attr.place == AttrPlacement::Outer);
114+
resolve_doc_path_on_(db, link, def.attr_id(), ns, is_outer)
110115
}
111116

112117
fn resolve_doc_path_on_(
113118
db: &dyn HirDatabase,
114119
link: &str,
115120
attr_id: AttrDefId,
116121
ns: Option<Namespace>,
122+
is_outer: bool,
117123
) -> Option<DocLinkDef> {
118124
let resolver = match attr_id {
119-
AttrDefId::ModuleId(it) => it.resolver(db.upcast()),
125+
AttrDefId::ModuleId(it) => {
126+
if is_outer {
127+
if let Some(parent) = Module::from(it).parent(db) {
128+
parent.id.resolver(db.upcast())
129+
} else {
130+
it.resolver(db.upcast())
131+
}
132+
} else {
133+
it.resolver(db.upcast())
134+
}
135+
}
120136
AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
121137
AttrDefId::AdtId(it) => it.resolver(db.upcast()),
122138
AttrDefId::FunctionId(it) => it.resolver(db.upcast()),

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)