]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! Attributes & documentation for hir types. |
2 | ||
3 | use hir_def::{ | |
781aab86 | 4 | attr::AttrsWithOwner, |
064997fb | 5 | item_scope::ItemInNs, |
781aab86 FG |
6 | path::{ModPath, Path}, |
7 | per_ns::Namespace, | |
8 | resolver::{HasResolver, Resolver, TypeNs}, | |
9 | AssocItemId, AttrDefId, ModuleDefId, | |
064997fb | 10 | }; |
781aab86 | 11 | use hir_expand::{hygiene::Hygiene, name::Name}; |
064997fb FG |
12 | use hir_ty::db::HirDatabase; |
13 | use syntax::{ast, AstNode}; | |
14 | ||
15 | use crate::{ | |
781aab86 FG |
16 | Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, |
17 | Field, Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, | |
18 | Trait, TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef, | |
064997fb FG |
19 | }; |
20 | ||
21 | pub trait HasAttrs { | |
22 | fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner; | |
781aab86 FG |
23 | #[doc(hidden)] |
24 | fn attr_id(self) -> AttrDefId; | |
064997fb FG |
25 | } |
26 | ||
27 | macro_rules! impl_has_attrs { | |
28 | ($(($def:ident, $def_id:ident),)*) => {$( | |
29 | impl HasAttrs for $def { | |
30 | fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { | |
31 | let def = AttrDefId::$def_id(self.into()); | |
fe692bf9 | 32 | db.attrs_with_owner(def) |
064997fb | 33 | } |
781aab86 FG |
34 | fn attr_id(self) -> AttrDefId { |
35 | AttrDefId::$def_id(self.into()) | |
064997fb FG |
36 | } |
37 | } | |
38 | )*}; | |
39 | } | |
40 | ||
41 | impl_has_attrs![ | |
42 | (Field, FieldId), | |
43 | (Variant, EnumVariantId), | |
44 | (Static, StaticId), | |
45 | (Const, ConstId), | |
46 | (Trait, TraitId), | |
353b0b11 | 47 | (TraitAlias, TraitAliasId), |
064997fb FG |
48 | (TypeAlias, TypeAliasId), |
49 | (Macro, MacroId), | |
50 | (Function, FunctionId), | |
51 | (Adt, AdtId), | |
52 | (Module, ModuleId), | |
53 | (GenericParam, GenericParamId), | |
54 | (Impl, ImplId), | |
781aab86 | 55 | (ExternCrateDecl, ExternCrateId), |
064997fb FG |
56 | ]; |
57 | ||
58 | macro_rules! impl_has_attrs_enum { | |
59 | ($($variant:ident),* for $enum:ident) => {$( | |
60 | impl HasAttrs for $variant { | |
61 | fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { | |
62 | $enum::$variant(self).attrs(db) | |
63 | } | |
781aab86 FG |
64 | fn attr_id(self) -> AttrDefId { |
65 | $enum::$variant(self).attr_id() | |
064997fb FG |
66 | } |
67 | } | |
68 | )*}; | |
69 | } | |
70 | ||
71 | impl_has_attrs_enum![Struct, Union, Enum for Adt]; | |
72 | impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam]; | |
73 | ||
74 | impl HasAttrs for AssocItem { | |
75 | fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { | |
76 | match self { | |
77 | AssocItem::Function(it) => it.attrs(db), | |
78 | AssocItem::Const(it) => it.attrs(db), | |
79 | AssocItem::TypeAlias(it) => it.attrs(db), | |
80 | } | |
81 | } | |
781aab86 | 82 | fn attr_id(self) -> AttrDefId { |
064997fb | 83 | match self { |
781aab86 FG |
84 | AssocItem::Function(it) => it.attr_id(), |
85 | AssocItem::Const(it) => it.attr_id(), | |
86 | AssocItem::TypeAlias(it) => it.attr_id(), | |
064997fb FG |
87 | } |
88 | } | |
89 | } | |
90 | ||
781aab86 FG |
91 | /// Resolves the item `link` points to in the scope of `def`. |
92 | pub fn resolve_doc_path_on( | |
93 | db: &dyn HirDatabase, | |
94 | def: impl HasAttrs, | |
95 | link: &str, | |
96 | ns: Option<Namespace>, | |
97 | ) -> Option<DocLinkDef> { | |
98 | // AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), | |
99 | // AttrDefId::EnumVariantId(it) => it.parent.resolver(db.upcast()), | |
100 | ||
101 | resolve_doc_path_on_(db, link, def.attr_id(), ns) | |
add651ee FG |
102 | } |
103 | ||
781aab86 | 104 | fn resolve_doc_path_on_( |
064997fb | 105 | db: &dyn HirDatabase, |
064997fb | 106 | link: &str, |
781aab86 | 107 | attr_id: AttrDefId, |
064997fb | 108 | ns: Option<Namespace>, |
781aab86 FG |
109 | ) -> Option<DocLinkDef> { |
110 | let resolver = match attr_id { | |
064997fb FG |
111 | AttrDefId::ModuleId(it) => it.resolver(db.upcast()), |
112 | AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), | |
113 | AttrDefId::AdtId(it) => it.resolver(db.upcast()), | |
114 | AttrDefId::FunctionId(it) => it.resolver(db.upcast()), | |
115 | AttrDefId::EnumVariantId(it) => it.parent.resolver(db.upcast()), | |
116 | AttrDefId::StaticId(it) => it.resolver(db.upcast()), | |
117 | AttrDefId::ConstId(it) => it.resolver(db.upcast()), | |
118 | AttrDefId::TraitId(it) => it.resolver(db.upcast()), | |
353b0b11 | 119 | AttrDefId::TraitAliasId(it) => it.resolver(db.upcast()), |
064997fb FG |
120 | AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()), |
121 | AttrDefId::ImplId(it) => it.resolver(db.upcast()), | |
122 | AttrDefId::ExternBlockId(it) => it.resolver(db.upcast()), | |
add651ee | 123 | AttrDefId::UseId(it) => it.resolver(db.upcast()), |
064997fb | 124 | AttrDefId::MacroId(it) => it.resolver(db.upcast()), |
add651ee | 125 | AttrDefId::ExternCrateId(it) => it.resolver(db.upcast()), |
781aab86 FG |
126 | AttrDefId::GenericParamId(_) => return None, |
127 | }; | |
128 | ||
129 | let mut modpath = modpath_from_str(db, link)?; | |
130 | ||
131 | let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); | |
132 | if resolved.is_none() { | |
133 | let last_name = modpath.pop_segment()?; | |
134 | resolve_assoc_or_field(db, resolver, modpath, last_name, ns) | |
135 | } else { | |
136 | let def = match ns { | |
137 | Some(Namespace::Types) => resolved.take_types(), | |
138 | Some(Namespace::Values) => resolved.take_values(), | |
139 | Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId), | |
140 | None => resolved.iter_items().next().map(|(it, _)| match it { | |
141 | ItemInNs::Types(it) => it, | |
142 | ItemInNs::Values(it) => it, | |
143 | ItemInNs::Macros(it) => ModuleDefId::MacroId(it), | |
144 | }), | |
145 | }; | |
146 | Some(DocLinkDef::ModuleDef(def?.into())) | |
147 | } | |
148 | } | |
149 | ||
150 | fn resolve_assoc_or_field( | |
151 | db: &dyn HirDatabase, | |
152 | resolver: Resolver, | |
153 | path: ModPath, | |
154 | name: Name, | |
155 | ns: Option<Namespace>, | |
156 | ) -> Option<DocLinkDef> { | |
157 | let path = Path::from_known_path_with_no_generic(path); | |
158 | // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the | |
159 | // trait itself. | |
160 | let base_def = resolver.resolve_path_in_type_ns_fully(db.upcast(), &path)?; | |
161 | ||
162 | let ty = match base_def { | |
163 | TypeNs::SelfType(id) => Impl::from(id).self_ty(db), | |
164 | TypeNs::GenericParam(_) => { | |
165 | // Even if this generic parameter has some trait bounds, rustdoc doesn't | |
166 | // resolve `name` to trait items. | |
167 | return None; | |
168 | } | |
169 | TypeNs::AdtId(id) | TypeNs::AdtSelfType(id) => Adt::from(id).ty(db), | |
170 | TypeNs::EnumVariantId(id) => { | |
171 | // Enum variants don't have path candidates. | |
172 | let variant = Variant::from(id); | |
173 | return resolve_field(db, variant.into(), name, ns); | |
174 | } | |
175 | TypeNs::TypeAliasId(id) => { | |
176 | let alias = TypeAlias::from(id); | |
177 | if alias.as_assoc_item(db).is_some() { | |
178 | // We don't normalize associated type aliases, so we have nothing to | |
179 | // resolve `name` to. | |
180 | return None; | |
181 | } | |
182 | alias.ty(db) | |
183 | } | |
184 | TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db), | |
185 | TypeNs::TraitId(id) => { | |
186 | // Doc paths in this context may only resolve to an item of this trait | |
187 | // (i.e. no items of its supertraits), so we need to handle them here | |
188 | // independently of others. | |
189 | return db.trait_data(id).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| { | |
190 | let def = match *assoc_id { | |
191 | AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()), | |
192 | AssocItemId::ConstId(it) => ModuleDef::Const(it.into()), | |
193 | AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), | |
194 | }; | |
195 | DocLinkDef::ModuleDef(def) | |
196 | }); | |
197 | } | |
198 | TypeNs::TraitAliasId(_) => { | |
199 | // XXX: Do these get resolved? | |
200 | return None; | |
064997fb | 201 | } |
064997fb FG |
202 | }; |
203 | ||
781aab86 FG |
204 | // FIXME: Resolve associated items here, e.g. `Option::map`. Note that associated items take |
205 | // precedence over fields. | |
206 | ||
207 | let variant_def = match ty.as_adt()? { | |
208 | Adt::Struct(it) => it.into(), | |
209 | Adt::Union(it) => it.into(), | |
210 | Adt::Enum(_) => return None, | |
211 | }; | |
212 | resolve_field(db, variant_def, name, ns) | |
213 | } | |
214 | ||
215 | fn resolve_field( | |
216 | db: &dyn HirDatabase, | |
217 | def: VariantDef, | |
218 | name: Name, | |
219 | ns: Option<Namespace>, | |
220 | ) -> Option<DocLinkDef> { | |
221 | if let Some(Namespace::Types | Namespace::Macros) = ns { | |
222 | return None; | |
223 | } | |
224 | def.fields(db).into_iter().find(|f| f.name(db) == name).map(DocLinkDef::Field) | |
225 | } | |
226 | ||
227 | fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option<ModPath> { | |
228 | // FIXME: this is not how we should get a mod path here. | |
229 | let try_get_modpath = |link: &str| { | |
9c376795 | 230 | let ast_path = ast::SourceFile::parse(&format!("type T = {link};")) |
064997fb FG |
231 | .syntax_node() |
232 | .descendants() | |
233 | .find_map(ast::Path::cast)?; | |
fe692bf9 | 234 | if ast_path.syntax().text() != link { |
064997fb FG |
235 | return None; |
236 | } | |
781aab86 | 237 | ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic()) |
064997fb FG |
238 | }; |
239 | ||
781aab86 FG |
240 | let full = try_get_modpath(link); |
241 | if full.is_some() { | |
242 | return full; | |
064997fb | 243 | } |
781aab86 FG |
244 | |
245 | // Tuple field names cannot be a part of `ModPath` usually, but rustdoc can | |
246 | // resolve doc paths like `TupleStruct::0`. | |
247 | // FIXME: Find a better way to handle these. | |
248 | let (base, maybe_tuple_field) = link.rsplit_once("::")?; | |
249 | let tuple_field = Name::new_tuple_field(maybe_tuple_field.parse().ok()?); | |
250 | let mut modpath = try_get_modpath(base)?; | |
251 | modpath.push_segment(tuple_field); | |
252 | Some(modpath) | |
064997fb | 253 | } |