1use std::ops::ControlFlow;
4
5use hir_def::{
6 AssocItemId, AttrDefId, ModuleDefId,
7 attr::AttrsWithOwner,
8 expr_store::path::Path,
9 item_scope::ItemInNs,
10 per_ns::Namespace,
11 resolver::{HasResolver, Resolver, TypeNs},
12};
13use hir_expand::{
14 mod_path::{ModPath, PathKind},
15 name::Name,
16};
17use hir_ty::{db::HirDatabase, method_resolution};
18
19use crate::{
20 Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
21 Field, Function, GenericParam, HasCrate, Impl, LifetimeParam, Macro, Module, ModuleDef, Static,
22 Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
23};
24
25pub trait HasAttrs {
26 fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner;
27 #[doc(hidden)]
28 fn attr_id(self) -> AttrDefId;
29}
30
31macro_rules! impl_has_attrs {
32 ($(($def:ident, $def_id:ident),)*) => {$(
33 impl HasAttrs for $def {
34 fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
35 let def = AttrDefId::$def_id(self.into());
36 AttrsWithOwner::new(db, def)
37 }
38 fn attr_id(self) -> AttrDefId {
39 AttrDefId::$def_id(self.into())
40 }
41 }
42 )*};
43}
44
45impl_has_attrs![
46 (Field, FieldId),
47 (Variant, EnumVariantId),
48 (Static, StaticId),
49 (Const, ConstId),
50 (Trait, TraitId),
51 (TypeAlias, TypeAliasId),
52 (Macro, MacroId),
53 (Function, FunctionId),
54 (Adt, AdtId),
55 (Module, ModuleId),
56 (GenericParam, GenericParamId),
57 (Impl, ImplId),
58 (ExternCrateDecl, ExternCrateId),
59];
60
61macro_rules! impl_has_attrs_enum {
62 ($($variant:ident),* for $enum:ident) => {$(
63 impl HasAttrs for $variant {
64 fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
65 $enum::$variant(self).attrs(db)
66 }
67 fn attr_id(self) -> AttrDefId {
68 $enum::$variant(self).attr_id()
69 }
70 }
71 )*};
72}
73
74impl_has_attrs_enum![Struct, Union, Enum for Adt];
75impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam];
76
77impl HasAttrs for AssocItem {
78 fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
79 match self {
80 AssocItem::Function(it) => it.attrs(db),
81 AssocItem::Const(it) => it.attrs(db),
82 AssocItem::TypeAlias(it) => it.attrs(db),
83 }
84 }
85 fn attr_id(self) -> AttrDefId {
86 match self {
87 AssocItem::Function(it) => it.attr_id(),
88 AssocItem::Const(it) => it.attr_id(),
89 AssocItem::TypeAlias(it) => it.attr_id(),
90 }
91 }
92}
93
94impl HasAttrs for crate::Crate {
95 fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
96 let def = AttrDefId::ModuleId(self.root_module().id);
97 AttrsWithOwner::new(db, def)
98 }
99 fn attr_id(self) -> AttrDefId {
100 AttrDefId::ModuleId(self.root_module().id)
101 }
102}
103
104pub fn resolve_doc_path_on(
106 db: &dyn HirDatabase,
107 def: impl HasAttrs + Copy,
108 link: &str,
109 ns: Option<Namespace>,
110 is_inner_doc: bool,
111) -> Option<DocLinkDef> {
112 resolve_doc_path_on_(db, link, def.attr_id(), ns, is_inner_doc)
113}
114
115fn resolve_doc_path_on_(
116 db: &dyn HirDatabase,
117 link: &str,
118 attr_id: AttrDefId,
119 ns: Option<Namespace>,
120 is_inner_doc: bool,
121) -> Option<DocLinkDef> {
122 let resolver = match attr_id {
123 AttrDefId::ModuleId(it) => {
124 if is_inner_doc {
125 it.resolver(db)
126 } else if let Some(parent) = Module::from(it).parent(db) {
127 parent.id.resolver(db)
128 } else {
129 it.resolver(db)
130 }
131 }
132 AttrDefId::FieldId(it) => it.parent.resolver(db),
133 AttrDefId::AdtId(it) => it.resolver(db),
134 AttrDefId::FunctionId(it) => it.resolver(db),
135 AttrDefId::EnumVariantId(it) => it.resolver(db),
136 AttrDefId::StaticId(it) => it.resolver(db),
137 AttrDefId::ConstId(it) => it.resolver(db),
138 AttrDefId::TraitId(it) => it.resolver(db),
139 AttrDefId::TypeAliasId(it) => it.resolver(db),
140 AttrDefId::ImplId(it) => it.resolver(db),
141 AttrDefId::ExternBlockId(it) => it.resolver(db),
142 AttrDefId::UseId(it) => it.resolver(db),
143 AttrDefId::MacroId(it) => it.resolver(db),
144 AttrDefId::ExternCrateId(it) => it.resolver(db),
145 AttrDefId::GenericParamId(_) => return None,
146 };
147
148 let mut modpath = doc_modpath_from_str(link)?;
149
150 let resolved = resolver.resolve_module_path_in_items(db, &modpath);
151 if resolved.is_none() {
152 let last_name = modpath.pop_segment()?;
153 resolve_assoc_or_field(db, resolver, modpath, last_name, ns)
154 } else {
155 let def = match ns {
156 Some(Namespace::Types) => resolved.take_types(),
157 Some(Namespace::Values) => resolved.take_values(),
158 Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId),
159 None => resolved.iter_items().next().map(|(it, _)| match it {
160 ItemInNs::Types(it) => it,
161 ItemInNs::Values(it) => it,
162 ItemInNs::Macros(it) => ModuleDefId::MacroId(it),
163 }),
164 };
165 Some(DocLinkDef::ModuleDef(def?.into()))
166 }
167}
168
169fn resolve_assoc_or_field(
170 db: &dyn HirDatabase,
171 resolver: Resolver<'_>,
172 path: ModPath,
173 name: Name,
174 ns: Option<Namespace>,
175) -> Option<DocLinkDef> {
176 let path = Path::from_known_path_with_no_generic(path);
177 let base_def = resolver.resolve_path_in_type_ns_fully(db, &path)?;
180
181 let ty = match base_def {
182 TypeNs::SelfType(id) => Impl::from(id).self_ty(db),
183 TypeNs::GenericParam(_) => {
184 return None;
187 }
188 TypeNs::AdtId(id) | TypeNs::AdtSelfType(id) => Adt::from(id).ty(db),
189 TypeNs::EnumVariantId(id) => {
190 let variant = Variant::from(id);
192 return resolve_field(db, variant.into(), name, ns);
193 }
194 TypeNs::TypeAliasId(id) => {
195 let alias = TypeAlias::from(id);
196 if alias.as_assoc_item(db).is_some() {
197 return None;
200 }
201 alias.ty(db)
202 }
203 TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db),
204 TypeNs::TraitId(id) => {
205 return id.trait_items(db).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| {
209 let def = match *assoc_id {
210 AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()),
211 AssocItemId::ConstId(it) => ModuleDef::Const(it.into()),
212 AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
213 };
214 DocLinkDef::ModuleDef(def)
215 });
216 }
217 TypeNs::ModuleId(_) => {
218 return None;
219 }
220 };
221
222 if let Some(assoc_item_def) = resolve_assoc_item(db, &ty, &name, ns) {
224 return Some(assoc_item_def);
225 }
226
227 if let Some(impl_trait_item_def) = resolve_impl_trait_item(db, resolver, &ty, &name, ns) {
228 return Some(impl_trait_item_def);
229 }
230
231 let variant_def = match ty.as_adt()? {
232 Adt::Struct(it) => it.into(),
233 Adt::Union(it) => it.into(),
234 Adt::Enum(_) => return None,
235 };
236 resolve_field(db, variant_def, name, ns)
237}
238
239fn resolve_assoc_item<'db>(
240 db: &'db dyn HirDatabase,
241 ty: &Type<'db>,
242 name: &Name,
243 ns: Option<Namespace>,
244) -> Option<DocLinkDef> {
245 ty.iterate_assoc_items(db, ty.krate(db), move |assoc_item| {
246 if assoc_item.name(db)? != *name {
247 return None;
248 }
249 as_module_def_if_namespace_matches(assoc_item, ns)
250 })
251}
252
253fn resolve_impl_trait_item<'db>(
254 db: &'db dyn HirDatabase,
255 resolver: Resolver<'_>,
256 ty: &Type<'db>,
257 name: &Name,
258 ns: Option<Namespace>,
259) -> Option<DocLinkDef> {
260 let canonical = ty.canonical();
261 let krate = ty.krate(db);
262 let environment = resolver
263 .generic_def()
264 .map_or_else(|| crate::TraitEnvironment::empty(krate.id), |d| db.trait_environment(d));
265 let traits_in_scope = resolver.traits_in_scope(db);
266
267 let mut result = None;
268
269 _ = method_resolution::iterate_path_candidates(
274 &canonical,
275 db,
276 environment,
277 &traits_in_scope,
278 method_resolution::VisibleFromModule::None,
279 Some(name),
280 &mut |_, assoc_item_id: AssocItemId, _| {
281 result = as_module_def_if_namespace_matches(assoc_item_id.into(), ns);
285
286 if result.is_some() { ControlFlow::Break(()) } else { ControlFlow::Continue(()) }
287 },
288 );
289
290 result
291}
292
293fn resolve_field(
294 db: &dyn HirDatabase,
295 def: VariantDef,
296 name: Name,
297 ns: Option<Namespace>,
298) -> Option<DocLinkDef> {
299 if let Some(Namespace::Types | Namespace::Macros) = ns {
300 return None;
301 }
302 def.fields(db).into_iter().find(|f| f.name(db) == name).map(DocLinkDef::Field)
303}
304
305fn as_module_def_if_namespace_matches(
306 assoc_item: AssocItem,
307 ns: Option<Namespace>,
308) -> Option<DocLinkDef> {
309 let (def, expected_ns) = match assoc_item {
310 AssocItem::Function(it) => (ModuleDef::Function(it), Namespace::Values),
311 AssocItem::Const(it) => (ModuleDef::Const(it), Namespace::Values),
312 AssocItem::TypeAlias(it) => (ModuleDef::TypeAlias(it), Namespace::Types),
313 };
314
315 (ns.unwrap_or(expected_ns) == expected_ns).then_some(DocLinkDef::ModuleDef(def))
316}
317
318fn doc_modpath_from_str(link: &str) -> Option<ModPath> {
319 let try_get_modpath = |link: &str| {
321 let mut parts = link.split("::");
322 let mut first_segment = None;
323 let kind = match parts.next()? {
324 "" => PathKind::Abs,
325 "crate" => PathKind::Crate,
326 "self" => PathKind::SELF,
327 "super" => {
328 let mut deg = 1;
329 for segment in parts.by_ref() {
330 if segment == "super" {
331 deg += 1;
332 } else {
333 first_segment = Some(segment);
334 break;
335 }
336 }
337 PathKind::Super(deg)
338 }
339 segment => {
340 first_segment = Some(segment);
341 PathKind::Plain
342 }
343 };
344 let parts = first_segment.into_iter().chain(parts).map(|segment| match segment.parse() {
345 Ok(idx) => Name::new_tuple_field(idx),
346 Err(_) => Name::new_root(segment.split_once('<').map_or(segment, |it| it.0)),
347 });
348 Some(ModPath::from_segments(kind, parts))
349 };
350 try_get_modpath(link)
351}