]> git.lizzy.rs Git - rust.git/blob - crates/hir/src/attrs.rs
Use `FileAstId<ast::Adt>` in nameres where appropriate instead
[rust.git] / crates / hir / src / attrs.rs
1 //! Attributes & documentation for hir types.
2
3 use either::Either;
4 use hir_def::{
5     attr::{AttrsWithOwner, Documentation},
6     item_scope::ItemInNs,
7     path::ModPath,
8     per_ns::PerNs,
9     resolver::HasResolver,
10     AttrDefId, GenericParamId, ModuleDefId,
11 };
12 use hir_expand::{hygiene::Hygiene, MacroDefId};
13 use hir_ty::db::HirDatabase;
14 use syntax::{ast, AstNode};
15
16 use crate::{
17     Adt, AssocItem, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam,
18     MacroDef, Module, ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, 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<Either<ModuleDef, MacroDef>>;
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<Either<ModuleDef, MacroDef>> {
51                 let def = AttrDefId::$def_id(self.into());
52                 resolve_doc_path(db, def, link, ns).map(|it| it.map_left(ModuleDef::from).map_right(MacroDef::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     (TypeAlias, TypeAliasId),
65     (MacroDef, MacroDefId),
66     (Function, FunctionId),
67     (Adt, AdtId),
68     (Module, ModuleId),
69     (GenericParam, GenericParamId),
70     (Impl, ImplId),
71 ];
72
73 macro_rules! impl_has_attrs_enum {
74     ($($variant:ident),* for $enum:ident) => {$(
75         impl HasAttrs for $variant {
76             fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
77                 $enum::$variant(self).attrs(db)
78             }
79             fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
80                 $enum::$variant(self).docs(db)
81             }
82             fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<Either<ModuleDef, MacroDef>> {
83                 $enum::$variant(self).resolve_doc_path(db, link, ns)
84             }
85         }
86     )*};
87 }
88
89 impl_has_attrs_enum![Struct, Union, Enum for Adt];
90 impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam];
91
92 impl HasAttrs for AssocItem {
93     fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
94         match self {
95             AssocItem::Function(it) => it.attrs(db),
96             AssocItem::Const(it) => it.attrs(db),
97             AssocItem::TypeAlias(it) => it.attrs(db),
98         }
99     }
100
101     fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
102         match self {
103             AssocItem::Function(it) => it.docs(db),
104             AssocItem::Const(it) => it.docs(db),
105             AssocItem::TypeAlias(it) => it.docs(db),
106         }
107     }
108
109     fn resolve_doc_path(
110         self,
111         db: &dyn HirDatabase,
112         link: &str,
113         ns: Option<Namespace>,
114     ) -> Option<Either<ModuleDef, MacroDef>> {
115         match self {
116             AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
117             AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
118             AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
119         }
120     }
121 }
122
123 fn resolve_doc_path(
124     db: &dyn HirDatabase,
125     def: AttrDefId,
126     link: &str,
127     ns: Option<Namespace>,
128 ) -> Option<Either<ModuleDefId, MacroDefId>> {
129     let resolver = match def {
130         AttrDefId::ModuleId(it) => it.resolver(db.upcast()),
131         AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
132         AttrDefId::AdtId(it) => it.resolver(db.upcast()),
133         AttrDefId::FunctionId(it) => it.resolver(db.upcast()),
134         AttrDefId::EnumVariantId(it) => it.parent.resolver(db.upcast()),
135         AttrDefId::StaticId(it) => it.resolver(db.upcast()),
136         AttrDefId::ConstId(it) => it.resolver(db.upcast()),
137         AttrDefId::TraitId(it) => it.resolver(db.upcast()),
138         AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()),
139         AttrDefId::ImplId(it) => it.resolver(db.upcast()),
140         AttrDefId::ExternBlockId(it) => it.resolver(db.upcast()),
141         AttrDefId::GenericParamId(it) => match it {
142             GenericParamId::TypeParamId(it) => it.parent,
143             GenericParamId::LifetimeParamId(it) => it.parent,
144             GenericParamId::ConstParamId(it) => it.parent,
145         }
146         .resolver(db.upcast()),
147         // FIXME
148         AttrDefId::MacroDefId(_) => return None,
149     };
150
151     let modpath = {
152         let ast_path = ast::SourceFile::parse(&format!("type T = {};", link))
153             .syntax_node()
154             .descendants()
155             .find_map(ast::Path::cast)?;
156         if ast_path.to_string() != link {
157             return None;
158         }
159         ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())?
160     };
161
162     let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
163     let resolved = if resolved == PerNs::none() {
164         resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)?
165     } else {
166         resolved
167     };
168     match ns {
169         Some(Namespace::Types) => resolved.take_types().map(Either::Left),
170         Some(Namespace::Values) => resolved.take_values().map(Either::Left),
171         Some(Namespace::Macros) => resolved.take_macros().map(Either::Right),
172         None => resolved.iter_items().next().map(|it| match it {
173             ItemInNs::Types(it) => Either::Left(it),
174             ItemInNs::Values(it) => Either::Left(it),
175             ItemInNs::Macros(it) => Either::Right(it),
176         }),
177     }
178 }