Skip to content

Commit e7be544

Browse files
bors[bot]Veykril
andauthored
Merge #9756
9756: internal: `resolve_doc_path` is able to resolve to macros r=Veykril a=Veykril bors r+ Co-authored-by: Lukas Wirth <[email protected]>
2 parents df0936b + 62ab737 commit e7be544

File tree

6 files changed

+121
-75
lines changed

6 files changed

+121
-75
lines changed

crates/hir/src/attrs.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
//! Attributes & documentation for hir types.
2+
3+
use either::Either;
24
use hir_def::{
35
attr::{AttrsWithOwner, Documentation},
6+
item_scope::ItemInNs,
47
path::ModPath,
58
per_ns::PerNs,
69
resolver::HasResolver,
710
AttrDefId, GenericParamId, ModuleDefId,
811
};
9-
use hir_expand::hygiene::Hygiene;
12+
use hir_expand::{hygiene::Hygiene, MacroDefId};
1013
use hir_ty::db::HirDatabase;
1114
use syntax::ast;
1215

@@ -23,7 +26,7 @@ pub trait HasAttrs {
2326
db: &dyn HirDatabase,
2427
link: &str,
2528
ns: Option<Namespace>,
26-
) -> Option<ModuleDef>;
29+
) -> Option<Either<ModuleDef, MacroDef>>;
2730
}
2831

2932
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
@@ -44,9 +47,9 @@ macro_rules! impl_has_attrs {
4447
let def = AttrDefId::$def_id(self.into());
4548
db.attrs(def).docs()
4649
}
47-
fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
50+
fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<Either<ModuleDef, MacroDef>> {
4851
let def = AttrDefId::$def_id(self.into());
49-
resolve_doc_path(db, def, link, ns).map(ModuleDef::from)
52+
resolve_doc_path(db, def, link, ns).map(|it| it.map_left(ModuleDef::from).map_right(MacroDef::from))
5053
}
5154
}
5255
)*};
@@ -76,7 +79,7 @@ macro_rules! impl_has_attrs_enum {
7679
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
7780
$enum::$variant(self).docs(db)
7881
}
79-
fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
82+
fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<Either<ModuleDef, MacroDef>> {
8083
$enum::$variant(self).resolve_doc_path(db, link, ns)
8184
}
8285
}
@@ -108,7 +111,7 @@ impl HasAttrs for AssocItem {
108111
db: &dyn HirDatabase,
109112
link: &str,
110113
ns: Option<Namespace>,
111-
) -> Option<ModuleDef> {
114+
) -> Option<Either<ModuleDef, MacroDef>> {
112115
match self {
113116
AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
114117
AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
@@ -122,7 +125,7 @@ fn resolve_doc_path(
122125
def: AttrDefId,
123126
link: &str,
124127
ns: Option<Namespace>,
125-
) -> Option<ModuleDefId> {
128+
) -> Option<Either<ModuleDefId, MacroDefId>> {
126129
let resolver = match def {
127130
AttrDefId::ModuleId(it) => it.resolver(db.upcast()),
128131
AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
@@ -140,6 +143,7 @@ fn resolve_doc_path(
140143
GenericParamId::ConstParamId(it) => it.parent,
141144
}
142145
.resolver(db.upcast()),
146+
// FIXME
143147
AttrDefId::MacroDefId(_) => return None,
144148
};
145149
let path = ast::Path::parse(link).ok()?;
@@ -151,9 +155,13 @@ fn resolve_doc_path(
151155
resolved
152156
};
153157
match ns {
154-
Some(Namespace::Types) => resolved.take_types(),
155-
Some(Namespace::Values) => resolved.take_values(),
156-
Some(Namespace::Macros) => None,
157-
None => resolved.iter_items().find_map(|it| it.as_module_def_id()),
158+
Some(Namespace::Types) => resolved.take_types().map(Either::Left),
159+
Some(Namespace::Values) => resolved.take_values().map(Either::Left),
160+
Some(Namespace::Macros) => resolved.take_macros().map(Either::Right),
161+
None => resolved.iter_items().next().map(|it| match it {
162+
ItemInNs::Types(it) => Either::Left(it),
163+
ItemInNs::Values(it) => Either::Left(it),
164+
ItemInNs::Macros(it) => Either::Right(it),
165+
}),
158166
}
159167
}

crates/ide/src/display/navigation_target.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ pub(crate) trait TryToNav {
7575
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget>;
7676
}
7777

78+
impl<T: TryToNav, U: TryToNav> TryToNav for Either<T, U> {
79+
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
80+
match self {
81+
Either::Left(it) => it.try_to_nav(db),
82+
Either::Right(it) => it.try_to_nav(db),
83+
}
84+
}
85+
}
86+
7887
impl NavigationTarget {
7988
pub fn focus_or_full_range(&self) -> TextRange {
8089
self.focus_range.unwrap_or(self.full_range)

crates/ide/src/doc_links.rs

Lines changed: 73 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ mod intra_doc_links;
44

55
use std::convert::{TryFrom, TryInto};
66

7+
use either::Either;
78
use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
89
use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions};
910
use stdx::format_to;
1011
use url::Url;
1112

1213
use hir::{
13-
db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasAttrs, ModuleDef,
14+
db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasAttrs, MacroDef,
15+
ModuleDef,
1416
};
1517
use ide_db::{
1618
defs::{Definition, NameClass, NameRefClass},
@@ -47,7 +49,7 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: Defin
4749
return rewritten;
4850
}
4951
if let Definition::ModuleDef(def) = definition {
50-
if let Some(target) = rewrite_url_link(db, def, target) {
52+
if let Some(target) = rewrite_url_link(db, Either::Left(def), target) {
5153
return (target, title.to_string());
5254
}
5355
}
@@ -169,7 +171,7 @@ pub(crate) fn resolve_doc_path_for_def(
169171
def: Definition,
170172
link: &str,
171173
ns: Option<hir::Namespace>,
172-
) -> Option<hir::ModuleDef> {
174+
) -> Option<Either<ModuleDef, MacroDef>> {
173175
match def {
174176
Definition::ModuleDef(def) => match def {
175177
hir::ModuleDef::Module(it) => it.resolve_doc_path(db, link, ns),
@@ -243,9 +245,9 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
243245
AssocItemContainer::Impl(i) => i.self_ty(db).as_adt()?.into(),
244246
};
245247
let frag = get_assoc_item_fragment(db, assoc_item)?;
246-
(def, Some(frag))
248+
(Either::Left(def), Some(frag))
247249
} else {
248-
(def, None)
250+
(Either::Left(def), None)
249251
}
250252
}
251253
Definition::Field(field) => {
@@ -254,10 +256,9 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
254256
hir::VariantDef::Union(it) => it.into(),
255257
hir::VariantDef::Variant(it) => it.into(),
256258
};
257-
(def, Some(format!("structfield.{}", field.name(db))))
259+
(Either::Left(def), Some(format!("structfield.{}", field.name(db))))
258260
}
259-
// FIXME macros
260-
Definition::Macro(_) => return None,
261+
Definition::Macro(makro) => (Either::Right(makro), None),
261262
// FIXME impls
262263
Definition::SelfType(_) => return None,
263264
Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) => return None,
@@ -270,7 +271,7 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
270271
url = url.join(&path).ok()?;
271272
}
272273

273-
url = url.join(&get_symbol_filename(db, &target)?).ok()?;
274+
url = url.join(&get_symbol_filename(db, target)?).ok()?;
274275
url.set_fragment(frag.as_deref());
275276

276277
Some(url.into())
@@ -292,24 +293,29 @@ fn rewrite_intra_doc_link(
292293
url = url.join(&path).ok()?;
293294
}
294295

295-
let (resolved, frag) = if let Some(assoc_item) = resolved.as_assoc_item(db) {
296-
let resolved = match assoc_item.container(db) {
297-
AssocItemContainer::Trait(t) => t.into(),
298-
AssocItemContainer::Impl(i) => i.self_ty(db).as_adt()?.into(),
296+
let (resolved, frag) =
297+
if let Some(assoc_item) = resolved.left().and_then(|it| it.as_assoc_item(db)) {
298+
let resolved = match assoc_item.container(db) {
299+
AssocItemContainer::Trait(t) => t.into(),
300+
AssocItemContainer::Impl(i) => i.self_ty(db).as_adt()?.into(),
301+
};
302+
let frag = get_assoc_item_fragment(db, assoc_item)?;
303+
(Either::Left(resolved), Some(frag))
304+
} else {
305+
(resolved, None)
299306
};
300-
let frag = get_assoc_item_fragment(db, assoc_item)?;
301-
(resolved, Some(frag))
302-
} else {
303-
(resolved, None)
304-
};
305-
url = url.join(&get_symbol_filename(db, &resolved)?).ok()?;
307+
url = url.join(&get_symbol_filename(db, resolved)?).ok()?;
306308
url.set_fragment(frag.as_deref());
307309

308310
Some((url.into(), strip_prefixes_suffixes(title).to_string()))
309311
}
310312

311313
/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`).
312-
fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<String> {
314+
fn rewrite_url_link(
315+
db: &RootDatabase,
316+
def: Either<ModuleDef, MacroDef>,
317+
target: &str,
318+
) -> Option<String> {
313319
if !(target.contains('#') || target.contains(".html")) {
314320
return None;
315321
}
@@ -321,25 +327,35 @@ fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<S
321327
url = url.join(&path).ok()?;
322328
}
323329

324-
url = url.join(&get_symbol_filename(db, &def)?).ok()?;
330+
url = url.join(&get_symbol_filename(db, def)?).ok()?;
325331
url.join(target).ok().map(Into::into)
326332
}
327333

328-
fn crate_of_def(db: &RootDatabase, def: ModuleDef) -> Option<Crate> {
334+
fn crate_of_def(db: &RootDatabase, def: Either<ModuleDef, MacroDef>) -> Option<Crate> {
329335
let krate = match def {
330336
// Definition::module gives back the parent module, we don't want that as it fails for root modules
331-
ModuleDef::Module(module) => module.krate(),
332-
_ => def.module(db)?.krate(),
337+
Either::Left(ModuleDef::Module(module)) => module.krate(),
338+
Either::Left(def) => def.module(db)?.krate(),
339+
Either::Right(def) => def.module(db)?.krate(),
333340
};
334341
Some(krate)
335342
}
336343

337-
fn mod_path_of_def(db: &RootDatabase, def: ModuleDef) -> Option<String> {
338-
def.canonical_module_path(db).map(|it| {
339-
let mut path = String::new();
340-
it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name));
341-
path
342-
})
344+
fn mod_path_of_def(db: &RootDatabase, def: Either<ModuleDef, MacroDef>) -> Option<String> {
345+
match def {
346+
Either::Left(def) => def.canonical_module_path(db).map(|it| {
347+
let mut path = String::new();
348+
it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name));
349+
path
350+
}),
351+
Either::Right(def) => {
352+
def.module(db).map(|it| it.path_to_root(db).into_iter().rev()).map(|it| {
353+
let mut path = String::new();
354+
it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name));
355+
path
356+
})
357+
}
358+
}
343359
}
344360

345361
/// Rewrites a markdown document, applying 'callback' to each link.
@@ -405,27 +421,34 @@ fn get_doc_base_url(db: &RootDatabase, krate: &Crate) -> Option<Url> {
405421
/// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
406422
/// ^^^^^^^^^^^^^^^^^^^
407423
/// ```
408-
fn get_symbol_filename(db: &dyn HirDatabase, definition: &ModuleDef) -> Option<String> {
409-
Some(match definition {
410-
ModuleDef::Adt(adt) => match adt {
411-
Adt::Struct(s) => format!("struct.{}.html", s.name(db)),
412-
Adt::Enum(e) => format!("enum.{}.html", e.name(db)),
413-
Adt::Union(u) => format!("union.{}.html", u.name(db)),
414-
},
415-
ModuleDef::Module(m) => match m.name(db) {
416-
Some(name) => format!("{}/index.html", name),
417-
None => String::from("index.html"),
424+
fn get_symbol_filename(
425+
db: &dyn HirDatabase,
426+
definition: Either<ModuleDef, MacroDef>,
427+
) -> Option<String> {
428+
let res = match definition {
429+
Either::Left(definition) => match definition {
430+
ModuleDef::Adt(adt) => match adt {
431+
Adt::Struct(s) => format!("struct.{}.html", s.name(db)),
432+
Adt::Enum(e) => format!("enum.{}.html", e.name(db)),
433+
Adt::Union(u) => format!("union.{}.html", u.name(db)),
434+
},
435+
ModuleDef::Module(m) => match m.name(db) {
436+
Some(name) => format!("{}/index.html", name),
437+
None => String::from("index.html"),
438+
},
439+
ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)),
440+
ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)),
441+
ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t.name()),
442+
ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)),
443+
ModuleDef::Variant(ev) => {
444+
format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db))
445+
}
446+
ModuleDef::Const(c) => format!("const.{}.html", c.name(db)?),
447+
ModuleDef::Static(s) => format!("static.{}.html", s.name(db)?),
418448
},
419-
ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)),
420-
ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)),
421-
ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t.name()),
422-
ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)),
423-
ModuleDef::Variant(ev) => {
424-
format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db))
425-
}
426-
ModuleDef::Const(c) => format!("const.{}.html", c.name(db)?),
427-
ModuleDef::Static(s) => format!("static.{}.html", s.name(db)?),
428-
})
449+
Either::Right(mac) => format!("macro.{}.html", mac.name(db)?),
450+
};
451+
Some(res)
429452
}
430453

431454
/// Get the fragment required to link to a specific field, method, associated type, or associated constant.

crates/ide/src/hover.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ pub(crate) fn hover(
150150
(file_id == position.file_id.into() && mapped_range.contains(position.offset)).then(||(mapped_range, link, ns))
151151
})?;
152152
range = Some(idl_range);
153-
resolve_doc_path_for_def(db, def, &link, ns).map(Definition::ModuleDef)
153+
Some(match resolve_doc_path_for_def(db,def, &link,ns)? {
154+
Either::Left(it) => Definition::ModuleDef(it),
155+
Either::Right(it) => Definition::Macro(it),
156+
})
154157
} else if let Some(attr) = token.ancestors().find_map(ast::Attr::cast) {
155158
if let res@Some(_) = try_hover_for_lint(&attr, &token) {
156159
return res;

crates/ide/src/syntax_highlighting/inject.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -238,19 +238,22 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri
238238
}
239239
}
240240

241-
fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag {
241+
fn module_def_to_hl_tag(def: Either<hir::ModuleDef, hir::MacroDef>) -> HlTag {
242242
let symbol = match def {
243-
hir::ModuleDef::Module(_) => SymbolKind::Module,
244-
hir::ModuleDef::Function(_) => SymbolKind::Function,
245-
hir::ModuleDef::Adt(hir::Adt::Struct(_)) => SymbolKind::Struct,
246-
hir::ModuleDef::Adt(hir::Adt::Enum(_)) => SymbolKind::Enum,
247-
hir::ModuleDef::Adt(hir::Adt::Union(_)) => SymbolKind::Union,
248-
hir::ModuleDef::Variant(_) => SymbolKind::Variant,
249-
hir::ModuleDef::Const(_) => SymbolKind::Const,
250-
hir::ModuleDef::Static(_) => SymbolKind::Static,
251-
hir::ModuleDef::Trait(_) => SymbolKind::Trait,
252-
hir::ModuleDef::TypeAlias(_) => SymbolKind::TypeAlias,
253-
hir::ModuleDef::BuiltinType(_) => return HlTag::BuiltinType,
243+
Either::Left(def) => match def {
244+
hir::ModuleDef::Module(_) => SymbolKind::Module,
245+
hir::ModuleDef::Function(_) => SymbolKind::Function,
246+
hir::ModuleDef::Adt(hir::Adt::Struct(_)) => SymbolKind::Struct,
247+
hir::ModuleDef::Adt(hir::Adt::Enum(_)) => SymbolKind::Enum,
248+
hir::ModuleDef::Adt(hir::Adt::Union(_)) => SymbolKind::Union,
249+
hir::ModuleDef::Variant(_) => SymbolKind::Variant,
250+
hir::ModuleDef::Const(_) => SymbolKind::Const,
251+
hir::ModuleDef::Static(_) => SymbolKind::Static,
252+
hir::ModuleDef::Trait(_) => SymbolKind::Trait,
253+
hir::ModuleDef::TypeAlias(_) => SymbolKind::TypeAlias,
254+
hir::ModuleDef::BuiltinType(_) => return HlTag::BuiltinType,
255+
},
256+
Either::Right(_) => SymbolKind::Macro,
254257
};
255258
HlTag::Symbol(symbol)
256259
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@
114114

115115
<span class="comment documentation">/// </span><span class="struct documentation injected intra_doc_link">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span>
116116
<span class="comment documentation">/// This function is &gt; </span><span class="function documentation injected intra_doc_link">[`all_the_links`](all_the_links)</span><span class="comment documentation"> &lt;</span>
117-
<span class="comment documentation">/// [`noop`](noop) is a macro below</span>
117+
<span class="comment documentation">/// </span><span class="macro documentation injected intra_doc_link">[`noop`](noop)</span><span class="comment documentation"> is a macro below</span>
118118
<span class="comment documentation">/// </span><span class="struct documentation injected intra_doc_link">[`Item`]</span><span class="comment documentation"> is a struct in the module </span><span class="module documentation injected intra_doc_link">[`module`]</span>
119119
<span class="comment documentation">///</span>
120120
<span class="comment documentation">/// [`Item`]: module::Item</span>

0 commit comments

Comments
 (0)