Skip to content

Commit 8b62486

Browse files
authored
Merge pull request #19507 from Hmikihiro/fix_module_doc_links
fix: resolve doc path from parent module if outer comments exist on module
2 parents 46a31fe + 7c7d440 commit 8b62486

File tree

11 files changed

+548
-149
lines changed

11 files changed

+548
-149
lines changed

crates/hir-expand/src/attrs.rs

+24-27
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! A higher level attributes based on TokenTree, with also some shortcuts.
2+
use std::iter;
23
use std::{borrow::Cow, fmt, ops};
34

45
use base_db::Crate;
@@ -122,16 +123,15 @@ impl RawAttrs {
122123
(None, entries @ Some(_)) => Self { entries },
123124
(Some(entries), None) => Self { entries: Some(entries.clone()) },
124125
(Some(a), Some(b)) => {
125-
let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
126+
let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1);
126127
let items = a
127128
.slice
128129
.iter()
129130
.cloned()
130131
.chain(b.slice.iter().map(|it| {
131132
let mut it = it.clone();
132-
it.id.id = (it.id.ast_index() as u32 + last_ast_index)
133-
| ((it.id.cfg_attr_index().unwrap_or(0) as u32)
134-
<< AttrId::AST_INDEX_BITS);
133+
let id = it.id.ast_index() + last_ast_index;
134+
it.id = AttrId::new(id, it.id.is_inner_attr());
135135
it
136136
}))
137137
.collect::<Vec<_>>();
@@ -175,25 +175,20 @@ pub struct AttrId {
175175
// FIXME: This only handles a single level of cfg_attr nesting
176176
// that is `#[cfg_attr(all(), cfg_attr(all(), cfg(any())))]` breaks again
177177
impl AttrId {
178-
const CFG_ATTR_BITS: usize = 7;
179-
const AST_INDEX_MASK: usize = 0x00FF_FFFF;
180-
const AST_INDEX_BITS: usize = Self::AST_INDEX_MASK.count_ones() as usize;
181-
const CFG_ATTR_SET_BITS: u32 = 1 << 31;
178+
const INNER_ATTR_SET_BIT: u32 = 1 << 31;
182179

183-
pub fn ast_index(&self) -> usize {
184-
self.id as usize & Self::AST_INDEX_MASK
180+
pub fn new(id: usize, is_inner: bool) -> Self {
181+
assert!(id <= !Self::INNER_ATTR_SET_BIT as usize);
182+
let id = id as u32;
183+
Self { id: if is_inner { id | Self::INNER_ATTR_SET_BIT } else { id } }
185184
}
186185

187-
pub fn cfg_attr_index(&self) -> Option<usize> {
188-
if self.id & Self::CFG_ATTR_SET_BITS == 0 {
189-
None
190-
} else {
191-
Some(self.id as usize >> Self::AST_INDEX_BITS)
192-
}
186+
pub fn ast_index(&self) -> usize {
187+
(self.id & !Self::INNER_ATTR_SET_BIT) as usize
193188
}
194189

195-
pub fn with_cfg_attr(self, idx: usize) -> AttrId {
196-
AttrId { id: self.id | ((idx as u32) << Self::AST_INDEX_BITS) | Self::CFG_ATTR_SET_BITS }
190+
pub fn is_inner_attr(&self) -> bool {
191+
self.id & Self::INNER_ATTR_SET_BIT != 0
197192
}
198193
}
199194

@@ -333,10 +328,7 @@ impl Attr {
333328
None => return smallvec![self.clone()],
334329
};
335330
let index = self.id;
336-
let attrs = parts
337-
.enumerate()
338-
.take(1 << AttrId::CFG_ATTR_BITS)
339-
.filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)));
331+
let attrs = parts.filter_map(|attr| Attr::from_tt(db, attr, index));
340332

341333
let cfg = TopSubtree::from_token_trees(subtree.top_subtree().delimiter, cfg);
342334
let cfg = CfgExpr::parse(&cfg);
@@ -467,13 +459,18 @@ fn unescape(s: &str) -> Option<Cow<'_, str>> {
467459
pub fn collect_attrs(
468460
owner: &dyn ast::HasAttrs,
469461
) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
470-
let inner_attrs = inner_attributes(owner.syntax()).into_iter().flatten();
471-
let outer_attrs =
472-
ast::AttrDocCommentIter::from_syntax_node(owner.syntax()).filter(|el| match el {
462+
let inner_attrs =
463+
inner_attributes(owner.syntax()).into_iter().flatten().zip(iter::repeat(true));
464+
let outer_attrs = ast::AttrDocCommentIter::from_syntax_node(owner.syntax())
465+
.filter(|el| match el {
473466
Either::Left(attr) => attr.kind().is_outer(),
474467
Either::Right(comment) => comment.is_outer(),
475-
});
476-
outer_attrs.chain(inner_attrs).enumerate().map(|(id, attr)| (AttrId { id: id as u32 }, attr))
468+
})
469+
.zip(iter::repeat(false));
470+
outer_attrs
471+
.chain(inner_attrs)
472+
.enumerate()
473+
.map(|(id, (attr, is_inner))| (AttrId::new(id, is_inner), attr))
477474
}
478475

479476
fn inner_attributes(

crates/hir/src/attrs.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,31 @@ 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>,
111+
is_inner_doc: bool,
111112
) -> Option<DocLinkDef> {
112-
resolve_doc_path_on_(db, link, def.attr_id(), ns)
113+
resolve_doc_path_on_(db, link, def.attr_id(), ns, is_inner_doc)
113114
}
114115

115116
fn resolve_doc_path_on_(
116117
db: &dyn HirDatabase,
117118
link: &str,
118119
attr_id: AttrDefId,
119120
ns: Option<Namespace>,
121+
is_inner_doc: bool,
120122
) -> Option<DocLinkDef> {
121123
let resolver = match attr_id {
122-
AttrDefId::ModuleId(it) => it.resolver(db),
124+
AttrDefId::ModuleId(it) => {
125+
if is_inner_doc {
126+
it.resolver(db)
127+
} else if let Some(parent) = Module::from(it).parent(db) {
128+
parent.id.resolver(db)
129+
} else {
130+
it.resolver(db)
131+
}
132+
}
123133
AttrDefId::FieldId(it) => it.parent.resolver(db),
124134
AttrDefId::AdtId(it) => it.resolver(db),
125135
AttrDefId::FunctionId(it) => it.resolver(db),

crates/ide-db/src/defs.rs

+38-25
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
77

88
use crate::RootDatabase;
9-
use crate::documentation::{Documentation, HasDocs};
9+
use crate::documentation::{DocsRangeMap, Documentation, HasDocs};
1010
use crate::famous_defs::FamousDefs;
1111
use arrayvec::ArrayVec;
1212
use either::Either;
@@ -21,7 +21,7 @@ use hir::{
2121
use span::Edition;
2222
use stdx::{format_to, impl_from};
2323
use syntax::{
24-
SyntaxKind, SyntaxNode, SyntaxToken,
24+
SyntaxKind, SyntaxNode, SyntaxToken, TextSize,
2525
ast::{self, AstNode},
2626
match_ast,
2727
};
@@ -210,29 +210,40 @@ impl Definition {
210210
famous_defs: Option<&FamousDefs<'_, '_>>,
211211
display_target: DisplayTarget,
212212
) -> Option<Documentation> {
213+
self.docs_with_rangemap(db, famous_defs, display_target).map(|(docs, _)| docs)
214+
}
215+
216+
pub fn docs_with_rangemap(
217+
&self,
218+
db: &RootDatabase,
219+
famous_defs: Option<&FamousDefs<'_, '_>>,
220+
display_target: DisplayTarget,
221+
) -> Option<(Documentation, Option<DocsRangeMap>)> {
213222
let docs = match self {
214-
Definition::Macro(it) => it.docs(db),
215-
Definition::Field(it) => it.docs(db),
216-
Definition::Module(it) => it.docs(db),
217-
Definition::Crate(it) => it.docs(db),
218-
Definition::Function(it) => it.docs(db),
219-
Definition::Adt(it) => it.docs(db),
220-
Definition::Variant(it) => it.docs(db),
221-
Definition::Const(it) => it.docs(db),
222-
Definition::Static(it) => it.docs(db),
223-
Definition::Trait(it) => it.docs(db),
224-
Definition::TraitAlias(it) => it.docs(db),
223+
Definition::Macro(it) => it.docs_with_rangemap(db),
224+
Definition::Field(it) => it.docs_with_rangemap(db),
225+
Definition::Module(it) => it.docs_with_rangemap(db),
226+
Definition::Crate(it) => it.docs_with_rangemap(db),
227+
Definition::Function(it) => it.docs_with_rangemap(db),
228+
Definition::Adt(it) => it.docs_with_rangemap(db),
229+
Definition::Variant(it) => it.docs_with_rangemap(db),
230+
Definition::Const(it) => it.docs_with_rangemap(db),
231+
Definition::Static(it) => it.docs_with_rangemap(db),
232+
Definition::Trait(it) => it.docs_with_rangemap(db),
233+
Definition::TraitAlias(it) => it.docs_with_rangemap(db),
225234
Definition::TypeAlias(it) => {
226-
it.docs(db).or_else(|| {
235+
it.docs_with_rangemap(db).or_else(|| {
227236
// docs are missing, try to fall back to the docs of the aliased item.
228237
let adt = it.ty(db).as_adt()?;
229-
let docs = adt.docs(db)?;
230-
let docs = format!(
231-
"*This is the documentation for* `{}`\n\n{}",
232-
adt.display(db, display_target),
233-
docs.as_str()
238+
let (docs, range_map) = adt.docs_with_rangemap(db)?;
239+
let header_docs = format!(
240+
"*This is the documentation for* `{}`\n\n",
241+
adt.display(db, display_target)
234242
);
235-
Some(Documentation::new(docs))
243+
let offset = TextSize::new(header_docs.len() as u32);
244+
let range_map = range_map.shift_docstring_line_range(offset);
245+
let docs = header_docs + docs.as_str();
246+
Some((Documentation::new(docs), range_map))
236247
})
237248
}
238249
Definition::BuiltinType(it) => {
@@ -241,17 +252,17 @@ impl Definition {
241252
let primitive_mod =
242253
format!("prim_{}", it.name().display(fd.0.db, display_target.edition));
243254
let doc_owner = find_std_module(fd, &primitive_mod, display_target.edition)?;
244-
doc_owner.docs(fd.0.db)
255+
doc_owner.docs_with_rangemap(fd.0.db)
245256
})
246257
}
247258
Definition::BuiltinLifetime(StaticLifetime) => None,
248259
Definition::Local(_) => None,
249260
Definition::SelfType(impl_def) => {
250-
impl_def.self_ty(db).as_adt().map(|adt| adt.docs(db))?
261+
impl_def.self_ty(db).as_adt().map(|adt| adt.docs_with_rangemap(db))?
251262
}
252263
Definition::GenericParam(_) => None,
253264
Definition::Label(_) => None,
254-
Definition::ExternCrateDecl(it) => it.docs(db),
265+
Definition::ExternCrateDecl(it) => it.docs_with_rangemap(db),
255266

256267
Definition::BuiltinAttr(it) => {
257268
let name = it.name(db);
@@ -276,7 +287,8 @@ impl Definition {
276287
name_value_str
277288
);
278289
}
279-
Some(Documentation::new(docs.replace('*', "\\*")))
290+
291+
return Some((Documentation::new(docs.replace('*', "\\*")), None));
280292
}
281293
Definition::ToolModule(_) => None,
282294
Definition::DeriveHelper(_) => None,
@@ -291,8 +303,9 @@ impl Definition {
291303
let trait_ = assoc.implemented_trait(db)?;
292304
let name = Some(assoc.name(db)?);
293305
let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
294-
item.docs(db)
306+
item.docs_with_rangemap(db)
295307
})
308+
.map(|(docs, range_map)| (docs, Some(range_map)))
296309
}
297310

298311
pub fn label(&self, db: &RootDatabase, display_target: DisplayTarget) -> String {

0 commit comments

Comments
 (0)