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