]> git.lizzy.rs Git - rust.git/blob - crates/ra_hir/src/from_source.rs
b3ed88b6b62b3c3db9d60cf04b07293588b218b0
[rust.git] / crates / ra_hir / src / from_source.rs
1 //! FIXME: write short doc here
2 use hir_def::{
3     child_by_source::ChildBySource, dyn_map::DynMap, keys, keys::Key, nameres::ModuleSource,
4     ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId,
5     StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
6 };
7 use hir_expand::{name::AsName, AstId, MacroDefId, MacroDefKind};
8 use ra_syntax::{
9     ast::{self, AstNode, NameOwner},
10     match_ast, SyntaxNode,
11 };
12
13 use crate::{
14     db::{AstDatabase, DefDatabase, HirDatabase},
15     Const, DefWithBody, Enum, EnumVariant, FieldSource, Function, ImplBlock, InFile, Local,
16     MacroDef, Module, Static, Struct, StructField, Trait, TypeAlias, TypeParam, Union,
17 };
18
19 pub trait FromSource: Sized {
20     type Ast;
21     fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self>;
22 }
23
24 pub trait FromSourceByContainer: Sized {
25     type Ast: AstNode + 'static;
26     type Id: Copy + 'static;
27     const KEY: Key<Self::Ast, Self::Id>;
28 }
29
30 impl<T: FromSourceByContainer> FromSource for T
31 where
32     T: From<<T as FromSourceByContainer>::Id>,
33 {
34     type Ast = <T as FromSourceByContainer>::Ast;
35     fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
36         analyze_container(db, src.as_ref().map(|it| it.syntax()))[T::KEY]
37             .get(&src)
38             .copied()
39             .map(Self::from)
40     }
41 }
42
43 macro_rules! from_source_by_container_impls {
44     ($(($hir:ident, $id:ident, $ast:path, $key:path)),* ,) => {$(
45         impl FromSourceByContainer for $hir {
46             type Ast = $ast;
47             type Id = $id;
48             const KEY: Key<Self::Ast, Self::Id> = $key;
49         }
50     )*}
51 }
52
53 from_source_by_container_impls![
54     (Struct, StructId, ast::StructDef, keys::STRUCT),
55     (Union, UnionId, ast::UnionDef, keys::UNION),
56     (Enum, EnumId, ast::EnumDef, keys::ENUM),
57     (Trait, TraitId, ast::TraitDef, keys::TRAIT),
58     (Function, FunctionId, ast::FnDef, keys::FUNCTION),
59     (Static, StaticId, ast::StaticDef, keys::STATIC),
60     (Const, ConstId, ast::ConstDef, keys::CONST),
61     (TypeAlias, TypeAliasId, ast::TypeAliasDef, keys::TYPE_ALIAS),
62     (ImplBlock, ImplId, ast::ImplBlock, keys::IMPL),
63 ];
64
65 impl FromSource for MacroDef {
66     type Ast = ast::MacroCall;
67     fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
68         let kind = MacroDefKind::Declarative;
69
70         let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
71         let module = Module::from_definition(db, InFile::new(src.file_id, module_src))?;
72         let krate = Some(module.krate().id);
73
74         let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value)));
75
76         let id: MacroDefId = MacroDefId { krate, ast_id, kind };
77         Some(MacroDef { id })
78     }
79 }
80
81 impl FromSource for EnumVariant {
82     type Ast = ast::EnumVariant;
83     fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
84         let parent_enum = src.value.parent_enum();
85         let src_enum = InFile { file_id: src.file_id, value: parent_enum };
86         let parent_enum = Enum::from_source(db, src_enum)?;
87         parent_enum.id.child_by_source(db)[keys::ENUM_VARIANT]
88             .get(&src)
89             .copied()
90             .map(EnumVariant::from)
91     }
92 }
93
94 impl FromSource for StructField {
95     type Ast = FieldSource;
96     fn from_source(db: &(impl DefDatabase + AstDatabase), src: InFile<Self::Ast>) -> Option<Self> {
97         let src = src.as_ref();
98
99         // FIXME this is buggy
100         let variant_id: VariantId = match src.value {
101             FieldSource::Named(field) => {
102                 let value = field.syntax().ancestors().find_map(ast::StructDef::cast)?;
103                 let src = InFile { file_id: src.file_id, value };
104                 let def = Struct::from_source(db, src)?;
105                 def.id.into()
106             }
107             FieldSource::Pos(field) => {
108                 let value = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?;
109                 let src = InFile { file_id: src.file_id, value };
110                 let def = EnumVariant::from_source(db, src)?;
111                 EnumVariantId::from(def).into()
112             }
113         };
114
115         let dyn_map = variant_id.child_by_source(db);
116         match src.value {
117             FieldSource::Pos(it) => dyn_map[keys::TUPLE_FIELD].get(&src.with_value(it.clone())),
118             FieldSource::Named(it) => dyn_map[keys::RECORD_FIELD].get(&src.with_value(it.clone())),
119         }
120         .copied()
121         .map(StructField::from)
122     }
123 }
124
125 impl Local {
126     pub fn from_source(db: &impl HirDatabase, src: InFile<ast::BindPat>) -> Option<Self> {
127         let file_id = src.file_id;
128         let parent: DefWithBody = src.value.syntax().ancestors().find_map(|it| {
129             let res = match_ast! {
130                 match it {
131                     ast::ConstDef(value) => { Const::from_source(db, InFile { value, file_id})?.into() },
132                     ast::StaticDef(value) => { Static::from_source(db, InFile { value, file_id})?.into() },
133                     ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.into() },
134                     _ => return None,
135                 }
136             };
137             Some(res)
138         })?;
139         let (_body, source_map) = db.body_with_source_map(parent.into());
140         let src = src.map(ast::Pat::from);
141         let pat_id = source_map.node_pat(src.as_ref())?;
142         Some(Local { parent, pat_id })
143     }
144 }
145
146 impl TypeParam {
147     pub fn from_source(db: &impl HirDatabase, src: InFile<ast::TypeParam>) -> Option<Self> {
148         let file_id = src.file_id;
149         let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| {
150             let res = match_ast! {
151                 match it {
152                     ast::FnDef(value) => { Function::from_source(db, InFile { value, file_id})?.id.into() },
153                     ast::StructDef(value) => { Struct::from_source(db, InFile { value, file_id})?.id.into() },
154                     ast::EnumDef(value) => { Enum::from_source(db, InFile { value, file_id})?.id.into() },
155                     ast::TraitDef(value) => { Trait::from_source(db, InFile { value, file_id})?.id.into() },
156                     ast::TypeAliasDef(value) => { TypeAlias::from_source(db, InFile { value, file_id})?.id.into() },
157                     ast::ImplBlock(value) => { ImplBlock::from_source(db, InFile { value, file_id})?.id.into() },
158                     _ => return None,
159                 }
160             };
161             Some(res)
162         })?;
163         let &id = parent.child_by_source(db)[keys::TYPE_PARAM].get(&src)?;
164         Some(TypeParam { id })
165     }
166 }
167
168 impl Module {
169     pub fn from_declaration(db: &impl DefDatabase, src: InFile<ast::Module>) -> Option<Self> {
170         let parent_declaration = src.value.syntax().ancestors().skip(1).find_map(ast::Module::cast);
171
172         let parent_module = match parent_declaration {
173             Some(parent_declaration) => {
174                 let src_parent = InFile { file_id: src.file_id, value: parent_declaration };
175                 Module::from_declaration(db, src_parent)
176             }
177             None => {
178                 let source_file = db.parse(src.file_id.original_file(db)).tree();
179                 let src_parent =
180                     InFile { file_id: src.file_id, value: ModuleSource::SourceFile(source_file) };
181                 Module::from_definition(db, src_parent)
182             }
183         }?;
184
185         let child_name = src.value.name()?.as_name();
186         let def_map = db.crate_def_map(parent_module.id.krate);
187         let child_id = def_map[parent_module.id.local_id].children.get(&child_name)?;
188         Some(parent_module.with_module_id(*child_id))
189     }
190
191     pub fn from_definition(db: &impl DefDatabase, src: InFile<ModuleSource>) -> Option<Self> {
192         match src.value {
193             ModuleSource::Module(ref module) => {
194                 assert!(!module.has_semi());
195                 return Module::from_declaration(
196                     db,
197                     InFile { file_id: src.file_id, value: module.clone() },
198                 );
199             }
200             ModuleSource::SourceFile(_) => (),
201         };
202
203         let original_file = src.file_id.original_file(db);
204
205         let (krate, local_id) = db.relevant_crates(original_file).iter().find_map(|&crate_id| {
206             let crate_def_map = db.crate_def_map(crate_id);
207             let local_id = crate_def_map.modules_for_file(original_file).next()?;
208             Some((crate_id, local_id))
209         })?;
210         Some(Module { id: ModuleId { krate, local_id } })
211     }
212 }
213
214 fn analyze_container(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> DynMap {
215     _analyze_container(db, src).unwrap_or_default()
216 }
217
218 fn _analyze_container(db: &impl DefDatabase, src: InFile<&SyntaxNode>) -> Option<DynMap> {
219     // FIXME: this doesn't try to handle nested declarations
220     for container in src.value.ancestors().skip(1) {
221         let res = match_ast! {
222             match container {
223                 ast::TraitDef(it) => {
224                     let c = Trait::from_source(db, src.with_value(it))?;
225                     c.id.child_by_source(db)
226                 },
227                 ast::ImplBlock(it) => {
228                     let c = ImplBlock::from_source(db, src.with_value(it))?;
229                     c.id.child_by_source(db)
230                 },
231                 ast::FnDef(it) => {
232                     let f = Function::from_source(db, src.with_value(it))?;
233                     DefWithBodyId::from(f.id)
234                         .child_by_source(db)
235                 },
236                 _ => { continue },
237             }
238         };
239         return Some(res);
240     }
241
242     let module_source = ModuleSource::from_child_node(db, src);
243     let c = Module::from_definition(db, src.with_value(module_source))?;
244     Some(c.id.child_by_source(db))
245 }