]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/hir/src/attrs.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / hir / src / attrs.rs
CommitLineData
064997fb
FG
1//! Attributes & documentation for hir types.
2
3use 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 11use hir_expand::{hygiene::Hygiene, name::Name};
064997fb
FG
12use hir_ty::db::HirDatabase;
13use syntax::{ast, AstNode};
14
15use 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
21pub 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
27macro_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
41impl_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
58macro_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
71impl_has_attrs_enum![Struct, Union, Enum for Adt];
72impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam];
73
74impl 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`.
92pub 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 104fn 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
150fn 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
215fn 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
227fn 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}