diff options
Diffstat (limited to 'crates/ra_hir/src/from_source.rs')
-rw-r--r-- | crates/ra_hir/src/from_source.rs | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs new file mode 100644 index 000000000..c6ad85fc7 --- /dev/null +++ b/crates/ra_hir/src/from_source.rs | |||
@@ -0,0 +1,211 @@ | |||
1 | use ra_db::{FileId, FilePosition}; | ||
2 | use ra_syntax::{ | ||
3 | algo::find_node_at_offset, | ||
4 | ast::{self, AstNode, NameOwner}, | ||
5 | SyntaxNode, | ||
6 | }; | ||
7 | |||
8 | use crate::{ | ||
9 | db::{AstDatabase, DefDatabase, HirDatabase}, | ||
10 | ids::{AstItemDef, LocationCtx}, | ||
11 | name::AsName, | ||
12 | Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module, | ||
13 | ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef, | ||
14 | }; | ||
15 | |||
16 | pub trait FromSource: Sized { | ||
17 | type Ast; | ||
18 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self>; | ||
19 | } | ||
20 | |||
21 | impl FromSource for Struct { | ||
22 | type Ast = ast::StructDef; | ||
23 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
24 | let id = from_source(db, src)?; | ||
25 | Some(Struct { id }) | ||
26 | } | ||
27 | } | ||
28 | impl FromSource for Union { | ||
29 | type Ast = ast::StructDef; | ||
30 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
31 | let id = from_source(db, src)?; | ||
32 | Some(Union { id }) | ||
33 | } | ||
34 | } | ||
35 | impl FromSource for Enum { | ||
36 | type Ast = ast::EnumDef; | ||
37 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
38 | let id = from_source(db, src)?; | ||
39 | Some(Enum { id }) | ||
40 | } | ||
41 | } | ||
42 | impl FromSource for Trait { | ||
43 | type Ast = ast::TraitDef; | ||
44 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
45 | let id = from_source(db, src)?; | ||
46 | Some(Trait { id }) | ||
47 | } | ||
48 | } | ||
49 | impl FromSource for Function { | ||
50 | type Ast = ast::FnDef; | ||
51 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
52 | let id = from_source(db, src)?; | ||
53 | Some(Function { id }) | ||
54 | } | ||
55 | } | ||
56 | impl FromSource for Const { | ||
57 | type Ast = ast::ConstDef; | ||
58 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
59 | let id = from_source(db, src)?; | ||
60 | Some(Const { id }) | ||
61 | } | ||
62 | } | ||
63 | impl FromSource for Static { | ||
64 | type Ast = ast::StaticDef; | ||
65 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
66 | let id = from_source(db, src)?; | ||
67 | Some(Static { id }) | ||
68 | } | ||
69 | } | ||
70 | impl FromSource for TypeAlias { | ||
71 | type Ast = ast::TypeAliasDef; | ||
72 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
73 | let id = from_source(db, src)?; | ||
74 | Some(TypeAlias { id }) | ||
75 | } | ||
76 | } | ||
77 | // FIXME: add impl FromSource for MacroDef | ||
78 | |||
79 | impl FromSource for ImplBlock { | ||
80 | type Ast = ast::ImplBlock; | ||
81 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
82 | let module_src = crate::ModuleSource::from_child_node( | ||
83 | db, | ||
84 | src.file_id.original_file(db), | ||
85 | &src.ast.syntax(), | ||
86 | ); | ||
87 | let module = Module::from_definition(db, Source { file_id: src.file_id, ast: module_src })?; | ||
88 | let impls = module.impl_blocks(db); | ||
89 | impls.into_iter().find(|b| b.source(db) == src) | ||
90 | } | ||
91 | } | ||
92 | |||
93 | impl FromSource for EnumVariant { | ||
94 | type Ast = ast::EnumVariant; | ||
95 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
96 | let parent_enum = src.ast.parent_enum(); | ||
97 | let src_enum = Source { file_id: src.file_id, ast: parent_enum }; | ||
98 | let variants = Enum::from_source(db, src_enum)?.variants(db); | ||
99 | variants.into_iter().find(|v| v.source(db) == src) | ||
100 | } | ||
101 | } | ||
102 | |||
103 | impl FromSource for StructField { | ||
104 | type Ast = FieldSource; | ||
105 | fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> { | ||
106 | let variant_def: VariantDef = match src.ast { | ||
107 | FieldSource::Named(ref field) => { | ||
108 | let ast = field.syntax().ancestors().find_map(ast::StructDef::cast)?; | ||
109 | let src = Source { file_id: src.file_id, ast }; | ||
110 | let def = Struct::from_source(db, src)?; | ||
111 | VariantDef::from(def) | ||
112 | } | ||
113 | FieldSource::Pos(ref field) => { | ||
114 | let ast = field.syntax().ancestors().find_map(ast::EnumVariant::cast)?; | ||
115 | let src = Source { file_id: src.file_id, ast }; | ||
116 | let def = EnumVariant::from_source(db, src)?; | ||
117 | VariantDef::from(def) | ||
118 | } | ||
119 | }; | ||
120 | variant_def | ||
121 | .variant_data(db) | ||
122 | .fields() | ||
123 | .into_iter() | ||
124 | .flat_map(|it| it.iter()) | ||
125 | .map(|(id, _)| StructField { parent: variant_def.clone(), id }) | ||
126 | .find(|f| f.source(db) == src) | ||
127 | } | ||
128 | } | ||
129 | |||
130 | // FIXME: simplify it | ||
131 | impl ModuleSource { | ||
132 | pub fn from_position( | ||
133 | db: &(impl DefDatabase + AstDatabase), | ||
134 | position: FilePosition, | ||
135 | ) -> ModuleSource { | ||
136 | let parse = db.parse(position.file_id); | ||
137 | match &find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset) { | ||
138 | Some(m) if !m.has_semi() => ModuleSource::Module(m.clone()), | ||
139 | _ => { | ||
140 | let source_file = parse.tree().to_owned(); | ||
141 | ModuleSource::SourceFile(source_file) | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | pub fn from_child_node( | ||
147 | db: &(impl DefDatabase + AstDatabase), | ||
148 | file_id: FileId, | ||
149 | child: &SyntaxNode, | ||
150 | ) -> ModuleSource { | ||
151 | if let Some(m) = child.ancestors().filter_map(ast::Module::cast).find(|it| !it.has_semi()) { | ||
152 | ModuleSource::Module(m.clone()) | ||
153 | } else { | ||
154 | let source_file = db.parse(file_id).tree().to_owned(); | ||
155 | ModuleSource::SourceFile(source_file) | ||
156 | } | ||
157 | } | ||
158 | |||
159 | pub fn from_file_id(db: &(impl DefDatabase + AstDatabase), file_id: FileId) -> ModuleSource { | ||
160 | let source_file = db.parse(file_id).tree().to_owned(); | ||
161 | ModuleSource::SourceFile(source_file) | ||
162 | } | ||
163 | } | ||
164 | |||
165 | impl Module { | ||
166 | pub fn from_declaration(db: &impl HirDatabase, src: Source<ast::Module>) -> Option<Self> { | ||
167 | let src_parent = Source { | ||
168 | file_id: src.file_id, | ||
169 | ast: ModuleSource::new(db, Some(src.file_id.original_file(db)), None), | ||
170 | }; | ||
171 | let parent_module = Module::from_definition(db, src_parent)?; | ||
172 | let child_name = src.ast.name()?; | ||
173 | parent_module.child(db, &child_name.as_name()) | ||
174 | } | ||
175 | |||
176 | pub fn from_definition( | ||
177 | db: &(impl DefDatabase + AstDatabase), | ||
178 | src: Source<ModuleSource>, | ||
179 | ) -> Option<Self> { | ||
180 | let decl_id = match src.ast { | ||
181 | ModuleSource::Module(ref module) => { | ||
182 | assert!(!module.has_semi()); | ||
183 | let ast_id_map = db.ast_id_map(src.file_id); | ||
184 | let item_id = ast_id_map.ast_id(module).with_file_id(src.file_id); | ||
185 | Some(item_id) | ||
186 | } | ||
187 | ModuleSource::SourceFile(_) => None, | ||
188 | }; | ||
189 | |||
190 | let source_root_id = db.file_source_root(src.file_id.original_file(db)); | ||
191 | db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map( | ||
192 | |krate| { | ||
193 | let def_map = db.crate_def_map(krate); | ||
194 | let module_id = def_map.find_module_by_source(src.file_id, decl_id)?; | ||
195 | Some(Module { krate, module_id }) | ||
196 | }, | ||
197 | ) | ||
198 | } | ||
199 | } | ||
200 | |||
201 | fn from_source<N, DEF>(db: &(impl DefDatabase + AstDatabase), src: Source<N>) -> Option<DEF> | ||
202 | where | ||
203 | N: AstNode, | ||
204 | DEF: AstItemDef<N>, | ||
205 | { | ||
206 | let module_src = | ||
207 | crate::ModuleSource::from_child_node(db, src.file_id.original_file(db), &src.ast.syntax()); | ||
208 | let module = Module::from_definition(db, Source { file_id: src.file_id, ast: module_src })?; | ||
209 | let ctx = LocationCtx::new(db, module, src.file_id); | ||
210 | Some(DEF::from_ast(ctx, &src.ast)) | ||
211 | } | ||