]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/hir/src/attrs.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / hir / src / attrs.rs
1 //! Attributes & documentation for hir types.
2
3 use hir_def::{
4 attr::{AttrsWithOwner, Documentation},
5 item_scope::ItemInNs,
6 path::ModPath,
7 per_ns::PerNs,
8 resolver::HasResolver,
9 AttrDefId, GenericParamId, ModuleDefId,
10 };
11 use hir_expand::hygiene::Hygiene;
12 use hir_ty::db::HirDatabase;
13 use syntax::{ast, AstNode};
14
15 use crate::{
16 Adt, AssocItem, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam,
17 Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias, TypeParam, Union,
18 Variant,
19 };
20
21 pub trait HasAttrs {
22 fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner;
23 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation>;
24 fn resolve_doc_path(
25 self,
26 db: &dyn HirDatabase,
27 link: &str,
28 ns: Option<Namespace>,
29 ) -> Option<ModuleDef>;
30 }
31
32 #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
33 pub enum Namespace {
34 Types,
35 Values,
36 Macros,
37 }
38
39 macro_rules! impl_has_attrs {
40 ($(($def:ident, $def_id:ident),)*) => {$(
41 impl HasAttrs for $def {
42 fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
43 let def = AttrDefId::$def_id(self.into());
44 db.attrs(def)
45 }
46 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
47 let def = AttrDefId::$def_id(self.into());
48 db.attrs(def).docs()
49 }
50 fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
51 let def = AttrDefId::$def_id(self.into());
52 resolve_doc_path(db, def, link, ns).map(ModuleDef::from)
53 }
54 }
55 )*};
56 }
57
58 impl_has_attrs![
59 (Field, FieldId),
60 (Variant, EnumVariantId),
61 (Static, StaticId),
62 (Const, ConstId),
63 (Trait, TraitId),
64 (TraitAlias, TraitAliasId),
65 (TypeAlias, TypeAliasId),
66 (Macro, MacroId),
67 (Function, FunctionId),
68 (Adt, AdtId),
69 (Module, ModuleId),
70 (GenericParam, GenericParamId),
71 (Impl, ImplId),
72 ];
73
74 macro_rules! impl_has_attrs_enum {
75 ($($variant:ident),* for $enum:ident) => {$(
76 impl HasAttrs for $variant {
77 fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
78 $enum::$variant(self).attrs(db)
79 }
80 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
81 $enum::$variant(self).docs(db)
82 }
83 fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
84 $enum::$variant(self).resolve_doc_path(db, link, ns)
85 }
86 }
87 )*};
88 }
89
90 impl_has_attrs_enum![Struct, Union, Enum for Adt];
91 impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam];
92
93 impl HasAttrs for AssocItem {
94 fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
95 match self {
96 AssocItem::Function(it) => it.attrs(db),
97 AssocItem::Const(it) => it.attrs(db),
98 AssocItem::TypeAlias(it) => it.attrs(db),
99 }
100 }
101
102 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
103 match self {
104 AssocItem::Function(it) => it.docs(db),
105 AssocItem::Const(it) => it.docs(db),
106 AssocItem::TypeAlias(it) => it.docs(db),
107 }
108 }
109
110 fn resolve_doc_path(
111 self,
112 db: &dyn HirDatabase,
113 link: &str,
114 ns: Option<Namespace>,
115 ) -> Option<ModuleDef> {
116 match self {
117 AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
118 AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
119 AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
120 }
121 }
122 }
123
124 fn resolve_doc_path(
125 db: &dyn HirDatabase,
126 def: AttrDefId,
127 link: &str,
128 ns: Option<Namespace>,
129 ) -> Option<ModuleDefId> {
130 let resolver = match def {
131 AttrDefId::ModuleId(it) => it.resolver(db.upcast()),
132 AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
133 AttrDefId::AdtId(it) => it.resolver(db.upcast()),
134 AttrDefId::FunctionId(it) => it.resolver(db.upcast()),
135 AttrDefId::EnumVariantId(it) => it.parent.resolver(db.upcast()),
136 AttrDefId::StaticId(it) => it.resolver(db.upcast()),
137 AttrDefId::ConstId(it) => it.resolver(db.upcast()),
138 AttrDefId::TraitId(it) => it.resolver(db.upcast()),
139 AttrDefId::TraitAliasId(it) => it.resolver(db.upcast()),
140 AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()),
141 AttrDefId::ImplId(it) => it.resolver(db.upcast()),
142 AttrDefId::ExternBlockId(it) => it.resolver(db.upcast()),
143 AttrDefId::MacroId(it) => it.resolver(db.upcast()),
144 AttrDefId::GenericParamId(it) => match it {
145 GenericParamId::TypeParamId(it) => it.parent(),
146 GenericParamId::ConstParamId(it) => it.parent(),
147 GenericParamId::LifetimeParamId(it) => it.parent,
148 }
149 .resolver(db.upcast()),
150 };
151
152 let modpath = {
153 // FIXME: this is not how we should get a mod path here
154 let ast_path = ast::SourceFile::parse(&format!("type T = {link};"))
155 .syntax_node()
156 .descendants()
157 .find_map(ast::Path::cast)?;
158 if ast_path.to_string() != link {
159 return None;
160 }
161 ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())?
162 };
163
164 let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
165 let resolved = if resolved == PerNs::none() {
166 resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)?
167 } else {
168 resolved
169 };
170 match ns {
171 Some(Namespace::Types) => resolved.take_types(),
172 Some(Namespace::Values) => resolved.take_values(),
173 Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId),
174 None => resolved.iter_items().next().map(|it| match it {
175 ItemInNs::Types(it) => it,
176 ItemInNs::Values(it) => it,
177 ItemInNs::Macros(it) => ModuleDefId::MacroId(it),
178 }),
179 }
180 }