aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/src/semantics.rs253
1 files changed, 197 insertions, 56 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 810c49d6f..3d78f71c1 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -83,6 +83,11 @@ impl PathResolution {
83/// Primary API to get semantic information, like types, from syntax trees. 83/// Primary API to get semantic information, like types, from syntax trees.
84pub struct Semantics<'db, DB> { 84pub struct Semantics<'db, DB> {
85 pub db: &'db DB, 85 pub db: &'db DB,
86 imp: SemanticsImpl<'db>,
87}
88
89pub struct SemanticsImpl<'db> {
90 pub db: &'db dyn HirDatabase,
86 s2d_cache: RefCell<SourceToDefCache>, 91 s2d_cache: RefCell<SourceToDefCache>,
87 cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>, 92 cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
88} 93}
@@ -95,20 +100,180 @@ impl<DB> fmt::Debug for Semantics<'_, DB> {
95 100
96impl<'db, DB: HirDatabase> Semantics<'db, DB> { 101impl<'db, DB: HirDatabase> Semantics<'db, DB> {
97 pub fn new(db: &DB) -> Semantics<DB> { 102 pub fn new(db: &DB) -> Semantics<DB> {
98 Semantics { db, s2d_cache: Default::default(), cache: Default::default() } 103 let impl_ = SemanticsImpl::new(db);
104 Semantics { db, imp: impl_ }
99 } 105 }
100 106
101 pub fn parse(&self, file_id: FileId) -> ast::SourceFile { 107 pub fn parse(&self, file_id: FileId) -> ast::SourceFile {
102 let tree = self.db.parse(file_id).tree(); 108 self.imp.parse(file_id)
103 self.cache(tree.syntax().clone(), file_id.into());
104 tree
105 } 109 }
106 110
107 pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST { 111 pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST {
108 let file_id = d.source().file_id; 112 let file_id = d.source().file_id;
109 let root = self.db.parse_or_expand(file_id).unwrap(); 113 let root = self.db.parse_or_expand(file_id).unwrap();
110 self.cache(root, file_id); 114 self.imp.cache(root, file_id);
111 d.ast(self.db) 115 d.ast(self.db.upcast())
116 }
117
118 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
119 self.imp.expand(macro_call)
120 }
121
122 pub fn expand_hypothetical(
123 &self,
124 actual_macro_call: &ast::MacroCall,
125 hypothetical_args: &ast::TokenTree,
126 token_to_map: SyntaxToken,
127 ) -> Option<(SyntaxNode, SyntaxToken)> {
128 self.imp.expand_hypothetical(actual_macro_call, hypothetical_args, token_to_map)
129 }
130
131 pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
132 self.imp.descend_into_macros(token)
133 }
134
135 pub fn descend_node_at_offset<N: ast::AstNode>(
136 &self,
137 node: &SyntaxNode,
138 offset: TextSize,
139 ) -> Option<N> {
140 self.imp.descend_node_at_offset(node, offset).find_map(N::cast)
141 }
142
143 pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
144 self.imp.original_range(node)
145 }
146
147 pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
148 self.imp.diagnostics_range(diagnostics)
149 }
150
151 pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
152 self.imp.ancestors_with_macros(node)
153 }
154
155 pub fn ancestors_at_offset_with_macros(
156 &self,
157 node: &SyntaxNode,
158 offset: TextSize,
159 ) -> impl Iterator<Item = SyntaxNode> + '_ {
160 self.imp.ancestors_at_offset_with_macros(node, offset)
161 }
162
163 /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*,
164 /// search up until it is of the target AstNode type
165 pub fn find_node_at_offset_with_macros<N: AstNode>(
166 &self,
167 node: &SyntaxNode,
168 offset: TextSize,
169 ) -> Option<N> {
170 self.imp.ancestors_at_offset_with_macros(node, offset).find_map(N::cast)
171 }
172
173 /// Find a AstNode by offset inside SyntaxNode, if it is inside *MacroCall*,
174 /// descend it and find again
175 pub fn find_node_at_offset_with_descend<N: AstNode>(
176 &self,
177 node: &SyntaxNode,
178 offset: TextSize,
179 ) -> Option<N> {
180 if let Some(it) = find_node_at_offset(&node, offset) {
181 return Some(it);
182 }
183
184 self.imp.descend_node_at_offset(node, offset).find_map(N::cast)
185 }
186
187 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
188 self.imp.type_of_expr(expr)
189 }
190
191 pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
192 self.imp.type_of_pat(pat)
193 }
194
195 pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
196 self.imp.resolve_method_call(call)
197 }
198
199 pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
200 self.imp.resolve_field(field)
201 }
202
203 pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<(Field, Option<Local>)> {
204 self.imp.resolve_record_field(field)
205 }
206
207 pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<Field> {
208 self.imp.resolve_record_field_pat(field)
209 }
210
211 pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
212 self.imp.resolve_macro_call(macro_call)
213 }
214
215 pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
216 self.imp.resolve_path(path)
217 }
218
219 pub fn resolve_variant(&self, record_lit: ast::RecordLit) -> Option<VariantId> {
220 self.imp.resolve_variant(record_lit)
221 }
222
223 pub fn lower_path(&self, path: &ast::Path) -> Option<Path> {
224 self.imp.lower_path(path)
225 }
226
227 pub fn resolve_bind_pat_to_const(&self, pat: &ast::BindPat) -> Option<ModuleDef> {
228 self.imp.resolve_bind_pat_to_const(pat)
229 }
230
231 // FIXME: use this instead?
232 // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
233
234 pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> {
235 self.imp.record_literal_missing_fields(literal)
236 }
237
238 pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
239 self.imp.record_pattern_missing_fields(pattern)
240 }
241
242 pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
243 let src = self.imp.find_file(src.syntax().clone()).with_value(src).cloned();
244 T::to_def(&self.imp, src)
245 }
246
247 pub fn to_module_def(&self, file: FileId) -> Option<Module> {
248 self.imp.to_module_def(file)
249 }
250
251 pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> {
252 self.imp.scope(node)
253 }
254
255 pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> {
256 self.imp.scope_at_offset(node, offset)
257 }
258
259 pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
260 self.imp.scope_for_def(def)
261 }
262
263 pub fn assert_contains_node(&self, node: &SyntaxNode) {
264 self.imp.assert_contains_node(node)
265 }
266}
267
268impl<'db> SemanticsImpl<'db> {
269 pub fn new(db: &'db dyn HirDatabase) -> Self {
270 Self { db, s2d_cache: Default::default(), cache: Default::default() }
271 }
272
273 pub fn parse(&self, file_id: FileId) -> ast::SourceFile {
274 let tree = self.db.parse(file_id).tree();
275 self.cache(tree.syntax().clone(), file_id.into());
276 tree
112 } 277 }
113 278
114 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { 279 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
@@ -130,9 +295,15 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
130 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); 295 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call);
131 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); 296 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
132 let krate = sa.resolver.krate()?; 297 let krate = sa.resolver.krate()?;
133 let macro_call_id = macro_call 298 let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
134 .as_call_id(self.db, krate, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; 299 sa.resolver.resolve_path_as_macro(self.db.upcast(), &path)
135 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) 300 })?;
301 hir_expand::db::expand_hypothetical(
302 self.db.upcast(),
303 macro_call_id,
304 hypothetical_args,
305 token_to_map,
306 )
136 } 307 }
137 308
138 pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { 309 pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
@@ -147,7 +318,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
147 return None; 318 return None;
148 } 319 }
149 let file_id = sa.expand(self.db, token.with_value(&macro_call))?; 320 let file_id = sa.expand(self.db, token.with_value(&macro_call))?;
150 let token = file_id.expansion_info(self.db)?.map_token_down(token.as_ref())?; 321 let token = file_id.expansion_info(self.db.upcast())?.map_token_down(token.as_ref())?;
151 322
152 self.cache(find_root(&token.value.parent()), token.file_id); 323 self.cache(find_root(&token.value.parent()), token.file_id);
153 324
@@ -159,15 +330,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
159 token.value 330 token.value
160 } 331 }
161 332
162 pub fn descend_node_at_offset<N: ast::AstNode>( 333 pub fn descend_node_at_offset(
163 &self, 334 &self,
164 node: &SyntaxNode, 335 node: &SyntaxNode,
165 offset: TextSize, 336 offset: TextSize,
166 ) -> Option<N> { 337 ) -> impl Iterator<Item = SyntaxNode> + '_ {
167 // Handle macro token cases 338 // Handle macro token cases
168 node.token_at_offset(offset) 339 node.token_at_offset(offset)
169 .map(|token| self.descend_into_macros(token)) 340 .map(|token| self.descend_into_macros(token))
170 .find_map(|it| self.ancestors_with_macros(it.parent()).find_map(N::cast)) 341 .map(|it| self.ancestors_with_macros(it.parent()))
342 .flatten()
171 } 343 }
172 344
173 pub fn original_range(&self, node: &SyntaxNode) -> FileRange { 345 pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
@@ -184,7 +356,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
184 356
185 pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { 357 pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
186 let node = self.find_file(node); 358 let node = self.find_file(node);
187 node.ancestors_with_macros(self.db).map(|it| it.value) 359 node.ancestors_with_macros(self.db.upcast()).map(|it| it.value)
188 } 360 }
189 361
190 pub fn ancestors_at_offset_with_macros( 362 pub fn ancestors_at_offset_with_macros(
@@ -197,29 +369,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
197 .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) 369 .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
198 } 370 }
199 371
200 /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*,
201 /// search up until it is of the target AstNode type
202 pub fn find_node_at_offset_with_macros<N: AstNode>(
203 &self,
204 node: &SyntaxNode,
205 offset: TextSize,
206 ) -> Option<N> {
207 self.ancestors_at_offset_with_macros(node, offset).find_map(N::cast)
208 }
209
210 /// Find a AstNode by offset inside SyntaxNode, if it is inside *MacroCall*,
211 /// descend it and find again
212 pub fn find_node_at_offset_with_descend<N: AstNode>(
213 &self,
214 node: &SyntaxNode,
215 offset: TextSize,
216 ) -> Option<N> {
217 if let Some(it) = find_node_at_offset(&node, offset) {
218 return Some(it);
219 }
220 self.descend_node_at_offset(&node, offset)
221 }
222
223 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { 372 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
224 self.analyze(expr.syntax()).type_of(self.db, &expr) 373 self.analyze(expr.syntax()).type_of(self.db, &expr)
225 } 374 }
@@ -267,9 +416,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
267 self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat) 416 self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
268 } 417 }
269 418
270 // FIXME: use this instead?
271 // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
272
273 pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> { 419 pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> {
274 self.analyze(literal.syntax()) 420 self.analyze(literal.syntax())
275 .record_literal_missing_fields(self.db, literal) 421 .record_literal_missing_fields(self.db, literal)
@@ -282,11 +428,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
282 .unwrap_or_default() 428 .unwrap_or_default()
283 } 429 }
284 430
285 pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
286 let src = self.find_file(src.syntax().clone()).with_value(src).cloned();
287 T::to_def(self, src)
288 }
289
290 fn with_ctx<F: FnOnce(&mut SourceToDefCtx) -> T, T>(&self, f: F) -> T { 431 fn with_ctx<F: FnOnce(&mut SourceToDefCtx) -> T, T>(&self, f: F) -> T {
291 let mut cache = self.s2d_cache.borrow_mut(); 432 let mut cache = self.s2d_cache.borrow_mut();
292 let mut ctx = SourceToDefCtx { db: self.db, cache: &mut *cache }; 433 let mut ctx = SourceToDefCtx { db: self.db, cache: &mut *cache };
@@ -310,7 +451,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
310 } 451 }
311 452
312 pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> { 453 pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
313 let resolver = def.id.resolver(self.db); 454 let resolver = def.id.resolver(self.db.upcast());
314 SemanticsScope { db: self.db, resolver } 455 SemanticsScope { db: self.db, resolver }
315 } 456 }
316 457
@@ -331,17 +472,17 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
331 ChildContainer::DefWithBodyId(def) => { 472 ChildContainer::DefWithBodyId(def) => {
332 return SourceAnalyzer::new_for_body(self.db, def, src, offset) 473 return SourceAnalyzer::new_for_body(self.db, def, src, offset)
333 } 474 }
334 ChildContainer::TraitId(it) => it.resolver(self.db), 475 ChildContainer::TraitId(it) => it.resolver(self.db.upcast()),
335 ChildContainer::ImplId(it) => it.resolver(self.db), 476 ChildContainer::ImplId(it) => it.resolver(self.db.upcast()),
336 ChildContainer::ModuleId(it) => it.resolver(self.db), 477 ChildContainer::ModuleId(it) => it.resolver(self.db.upcast()),
337 ChildContainer::EnumId(it) => it.resolver(self.db), 478 ChildContainer::EnumId(it) => it.resolver(self.db.upcast()),
338 ChildContainer::VariantId(it) => it.resolver(self.db), 479 ChildContainer::VariantId(it) => it.resolver(self.db.upcast()),
339 ChildContainer::GenericDefId(it) => it.resolver(self.db), 480 ChildContainer::GenericDefId(it) => it.resolver(self.db.upcast()),
340 }; 481 };
341 SourceAnalyzer::new_for_resolver(resolver, src) 482 SourceAnalyzer::new_for_resolver(resolver, src)
342 } 483 }
343 484
344 fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { 485 pub fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) {
345 assert!(root_node.parent().is_none()); 486 assert!(root_node.parent().is_none());
346 let mut cache = self.cache.borrow_mut(); 487 let mut cache = self.cache.borrow_mut();
347 let prev = cache.insert(root_node, file_id); 488 let prev = cache.insert(root_node, file_id);
@@ -357,7 +498,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
357 cache.get(root_node).copied() 498 cache.get(root_node).copied()
358 } 499 }
359 500
360 fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> { 501 pub fn find_file(&self, node: SyntaxNode) -> InFile<SyntaxNode> {
361 let root_node = find_root(&node); 502 let root_node = find_root(&node);
362 let file_id = self.lookup(&root_node).unwrap_or_else(|| { 503 let file_id = self.lookup(&root_node).unwrap_or_else(|| {
363 panic!( 504 panic!(
@@ -382,14 +523,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
382pub trait ToDef: AstNode + Clone { 523pub trait ToDef: AstNode + Clone {
383 type Def; 524 type Def;
384 525
385 fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: InFile<Self>) -> Option<Self::Def>; 526 fn to_def(sema: &SemanticsImpl, src: InFile<Self>) -> Option<Self::Def>;
386} 527}
387 528
388macro_rules! to_def_impls { 529macro_rules! to_def_impls {
389 ($(($def:path, $ast:path, $meth:ident)),* ,) => {$( 530 ($(($def:path, $ast:path, $meth:ident)),* ,) => {$(
390 impl ToDef for $ast { 531 impl ToDef for $ast {
391 type Def = $def; 532 type Def = $def;
392 fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: InFile<Self>) -> Option<Self::Def> { 533 fn to_def(sema: &SemanticsImpl, src: InFile<Self>) -> Option<Self::Def> {
393 sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from) 534 sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from)
394 } 535 }
395 } 536 }