aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/source_analyzer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/source_analyzer.rs')
-rw-r--r--crates/ra_hir/src/source_analyzer.rs222
1 files changed, 79 insertions, 143 deletions
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index efa3f8a79..b655e2c32 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -14,29 +14,27 @@ use hir_def::{
14 BodySourceMap, 14 BodySourceMap,
15 }, 15 },
16 expr::{ExprId, PatId}, 16 expr::{ExprId, PatId},
17 resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, 17 resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
18 AsMacroCall, DefWithBodyId, TraitId, 18 AsMacroCall, DefWithBodyId,
19}; 19};
20use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile, MacroCallId}; 20use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
21use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; 21use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment};
22use ra_syntax::{ 22use ra_syntax::{
23 ast::{self, AstNode}, 23 ast::{self, AstNode},
24 AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, 24 AstPtr, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit,
25}; 25};
26use rustc_hash::FxHashSet;
27 26
28use crate::{ 27use crate::{
29 db::HirDatabase, Adt, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, Name, Path, 28 db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, Path, Static, Struct,
30 ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, 29 Trait, Type, TypeAlias, TypeParam,
31}; 30};
32 31
33/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of 32/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
34/// original source files. It should not be used inside the HIR itself. 33/// original source files. It should not be used inside the HIR itself.
35#[derive(Debug)] 34#[derive(Debug)]
36pub struct SourceAnalyzer { 35pub(crate) struct SourceAnalyzer {
37 file_id: HirFileId, 36 file_id: HirFileId,
38 resolver: Resolver, 37 pub(crate) resolver: Resolver,
39 body_owner: Option<DefWithBody>,
40 body_source_map: Option<Arc<BodySourceMap>>, 38 body_source_map: Option<Arc<BodySourceMap>>,
41 infer: Option<Arc<InferenceResult>>, 39 infer: Option<Arc<InferenceResult>>,
42 scopes: Option<Arc<ExprScopes>>, 40 scopes: Option<Arc<ExprScopes>>,
@@ -55,57 +53,13 @@ pub enum PathResolution {
55 AssocItem(crate::AssocItem), 53 AssocItem(crate::AssocItem),
56} 54}
57 55
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct ScopeEntryWithSyntax {
60 pub(crate) name: Name,
61 pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>,
62}
63
64impl ScopeEntryWithSyntax {
65 pub fn name(&self) -> &Name {
66 &self.name
67 }
68
69 pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> {
70 self.ptr
71 }
72}
73
74#[derive(Debug)] 56#[derive(Debug)]
75pub struct ReferenceDescriptor { 57pub struct ReferenceDescriptor {
76 pub range: TextRange, 58 pub range: TextRange,
77 pub name: String, 59 pub name: String,
78} 60}
79 61
80#[derive(Debug)]
81pub struct Expansion {
82 macro_call_id: MacroCallId,
83}
84
85impl Expansion {
86 pub fn map_token_down(
87 &self,
88 db: &impl HirDatabase,
89 token: InFile<&SyntaxToken>,
90 ) -> Option<InFile<SyntaxToken>> {
91 let exp_info = self.file_id().expansion_info(db)?;
92 exp_info.map_token_down(token)
93 }
94
95 pub fn file_id(&self) -> HirFileId {
96 self.macro_call_id.as_file()
97 }
98}
99
100impl SourceAnalyzer { 62impl SourceAnalyzer {
101 pub fn new(
102 db: &impl HirDatabase,
103 node: InFile<&SyntaxNode>,
104 offset: Option<TextUnit>,
105 ) -> SourceAnalyzer {
106 crate::source_binder::SourceBinder::new(db).analyze(node, offset)
107 }
108
109 pub(crate) fn new_for_body( 63 pub(crate) fn new_for_body(
110 db: &impl HirDatabase, 64 db: &impl HirDatabase,
111 def: DefWithBodyId, 65 def: DefWithBodyId,
@@ -121,7 +75,6 @@ impl SourceAnalyzer {
121 let resolver = resolver_for_scope(db, def, scope); 75 let resolver = resolver_for_scope(db, def, scope);
122 SourceAnalyzer { 76 SourceAnalyzer {
123 resolver, 77 resolver,
124 body_owner: Some(def.into()),
125 body_source_map: Some(source_map), 78 body_source_map: Some(source_map),
126 infer: Some(db.infer(def)), 79 infer: Some(db.infer(def)),
127 scopes: Some(scopes), 80 scopes: Some(scopes),
@@ -135,7 +88,6 @@ impl SourceAnalyzer {
135 ) -> SourceAnalyzer { 88 ) -> SourceAnalyzer {
136 SourceAnalyzer { 89 SourceAnalyzer {
137 resolver, 90 resolver,
138 body_owner: None,
139 body_source_map: None, 91 body_source_map: None,
140 infer: None, 92 infer: None,
141 scopes: None, 93 scopes: None,
@@ -143,10 +95,6 @@ impl SourceAnalyzer {
143 } 95 }
144 } 96 }
145 97
146 pub fn module(&self) -> Option<crate::code_model::Module> {
147 Some(crate::code_model::Module { id: self.resolver.module()? })
148 }
149
150 fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { 98 fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> {
151 let src = InFile { file_id: self.file_id, value: expr }; 99 let src = InFile { file_id: self.file_id, value: expr };
152 self.body_source_map.as_ref()?.node_expr(src) 100 self.body_source_map.as_ref()?.node_expr(src)
@@ -180,7 +128,7 @@ impl SourceAnalyzer {
180 TraitEnvironment::lower(db, &self.resolver) 128 TraitEnvironment::lower(db, &self.resolver)
181 } 129 }
182 130
183 pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { 131 pub(crate) fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> {
184 let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { 132 let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) {
185 self.body_source_map.as_ref()?.node_expr(expr.as_ref())? 133 self.body_source_map.as_ref()?.node_expr(expr.as_ref())?
186 } else { 134 } else {
@@ -192,24 +140,27 @@ impl SourceAnalyzer {
192 Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) 140 Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
193 } 141 }
194 142
195 pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> { 143 pub(crate) fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option<Type> {
196 let pat_id = self.pat_id(pat)?; 144 let pat_id = self.pat_id(pat)?;
197 let ty = self.infer.as_ref()?[pat_id].clone(); 145 let ty = self.infer.as_ref()?[pat_id].clone();
198 let environment = self.trait_env(db); 146 let environment = self.trait_env(db);
199 Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) 147 Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
200 } 148 }
201 149
202 pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { 150 pub(crate) fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
203 let expr_id = self.expr_id(&call.clone().into())?; 151 let expr_id = self.expr_id(&call.clone().into())?;
204 self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) 152 self.infer.as_ref()?.method_resolution(expr_id).map(Function::from)
205 } 153 }
206 154
207 pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { 155 pub(crate) fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> {
208 let expr_id = self.expr_id(&field.clone().into())?; 156 let expr_id = self.expr_id(&field.clone().into())?;
209 self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) 157 self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
210 } 158 }
211 159
212 pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<crate::StructField> { 160 pub(crate) fn resolve_record_field(
161 &self,
162 field: &ast::RecordField,
163 ) -> Option<crate::StructField> {
213 let expr_id = match field.expr() { 164 let expr_id = match field.expr() {
214 Some(it) => self.expr_id(&it)?, 165 Some(it) => self.expr_id(&it)?,
215 None => { 166 None => {
@@ -220,17 +171,23 @@ impl SourceAnalyzer {
220 self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) 171 self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into())
221 } 172 }
222 173
223 pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<crate::VariantDef> { 174 pub(crate) fn resolve_record_literal(
175 &self,
176 record_lit: &ast::RecordLit,
177 ) -> Option<crate::VariantDef> {
224 let expr_id = self.expr_id(&record_lit.clone().into())?; 178 let expr_id = self.expr_id(&record_lit.clone().into())?;
225 self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) 179 self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into())
226 } 180 }
227 181
228 pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<crate::VariantDef> { 182 pub(crate) fn resolve_record_pattern(
183 &self,
184 record_pat: &ast::RecordPat,
185 ) -> Option<crate::VariantDef> {
229 let pat_id = self.pat_id(&record_pat.clone().into())?; 186 let pat_id = self.pat_id(&record_pat.clone().into())?;
230 self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) 187 self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into())
231 } 188 }
232 189
233 pub fn resolve_macro_call( 190 pub(crate) fn resolve_macro_call(
234 &self, 191 &self,
235 db: &impl HirDatabase, 192 db: &impl HirDatabase,
236 macro_call: InFile<&ast::MacroCall>, 193 macro_call: InFile<&ast::MacroCall>,
@@ -240,52 +197,11 @@ impl SourceAnalyzer {
240 self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) 197 self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into())
241 } 198 }
242 199
243 pub fn resolve_hir_path( 200 pub(crate) fn resolve_path(
244 &self, 201 &self,
245 db: &impl HirDatabase, 202 db: &impl HirDatabase,
246 path: &crate::Path, 203 path: &ast::Path,
247 ) -> Option<PathResolution> { 204 ) -> Option<PathResolution> {
248 let types =
249 self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty {
250 TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
251 TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }),
252 TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
253 PathResolution::Def(Adt::from(it).into())
254 }
255 TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
256 TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
257 TypeNs::BuiltinType(it) => PathResolution::Def(it.into()),
258 TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
259 });
260 let values =
261 self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| {
262 let res = match val {
263 ValueNs::LocalBinding(pat_id) => {
264 let var = Local { parent: self.body_owner?, pat_id };
265 PathResolution::Local(var)
266 }
267 ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()),
268 ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()),
269 ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()),
270 ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()),
271 ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
272 };
273 Some(res)
274 });
275
276 let items = self
277 .resolver
278 .resolve_module_path_in_items(db, path.mod_path())
279 .take_types()
280 .map(|it| PathResolution::Def(it.into()));
281 types.or(values).or(items).or_else(|| {
282 self.resolver
283 .resolve_path_as_macro(db, path.mod_path())
284 .map(|def| PathResolution::Macro(def.into()))
285 })
286 }
287
288 pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
289 if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { 205 if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) {
290 let expr_id = self.expr_id(&path_expr.into())?; 206 let expr_id = self.expr_id(&path_expr.into())?;
291 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { 207 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) {
@@ -300,40 +216,24 @@ impl SourceAnalyzer {
300 } 216 }
301 // This must be a normal source file rather than macro file. 217 // This must be a normal source file rather than macro file.
302 let hir_path = crate::Path::from_ast(path.clone())?; 218 let hir_path = crate::Path::from_ast(path.clone())?;
303 self.resolve_hir_path(db, &hir_path) 219 resolve_hir_path(db, &self.resolver, &hir_path)
304 } 220 }
305 221
306 fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { 222 fn resolve_local_name(
223 &self,
224 name_ref: &ast::NameRef,
225 ) -> Option<Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>> {
307 let name = name_ref.as_name(); 226 let name = name_ref.as_name();
308 let source_map = self.body_source_map.as_ref()?; 227 let source_map = self.body_source_map.as_ref()?;
309 let scopes = self.scopes.as_ref()?; 228 let scopes = self.scopes.as_ref()?;
310 let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?; 229 let scope = scope_for(scopes, source_map, InFile::new(self.file_id, name_ref.syntax()))?;
311 let entry = scopes.resolve_name_in_scope(scope, &name)?; 230 let entry = scopes.resolve_name_in_scope(scope, &name)?;
312 Some(ScopeEntryWithSyntax { 231 Some(source_map.pat_syntax(entry.pat())?.value)
313 name: entry.name().clone(),
314 ptr: source_map.pat_syntax(entry.pat())?.value,
315 })
316 }
317
318 pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) {
319 self.resolver.process_all_names(db, &mut |name, def| {
320 let def = match def {
321 resolver::ScopeDef::PerNs(it) => it.into(),
322 resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()),
323 resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
324 resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }),
325 resolver::ScopeDef::Local(pat_id) => {
326 let parent = self.resolver.body_owner().unwrap().into();
327 ScopeDef::Local(Local { parent, pat_id })
328 }
329 };
330 f(name, def)
331 })
332 } 232 }
333 233
334 // FIXME: we only use this in `inline_local_variable` assist, ideally, we 234 // FIXME: we only use this in `inline_local_variable` assist, ideally, we
335 // should switch to general reference search infra there. 235 // should switch to general reference search infra there.
336 pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { 236 pub(crate) fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
337 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); 237 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
338 let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); 238 let ptr = Either::Left(AstPtr::new(&ast::Pat::from(pat.clone())));
339 fn_def 239 fn_def
@@ -342,7 +242,7 @@ impl SourceAnalyzer {
342 .filter_map(ast::NameRef::cast) 242 .filter_map(ast::NameRef::cast)
343 .filter(|name_ref| match self.resolve_local_name(&name_ref) { 243 .filter(|name_ref| match self.resolve_local_name(&name_ref) {
344 None => false, 244 None => false,
345 Some(entry) => entry.ptr() == ptr, 245 Some(d_ptr) => d_ptr == ptr,
346 }) 246 })
347 .map(|name_ref| ReferenceDescriptor { 247 .map(|name_ref| ReferenceDescriptor {
348 name: name_ref.text().to_string(), 248 name: name_ref.text().to_string(),
@@ -351,19 +251,14 @@ impl SourceAnalyzer {
351 .collect() 251 .collect()
352 } 252 }
353 253
354 /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type 254 pub(crate) fn expand(
355 pub fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet<TraitId> {
356 self.resolver.traits_in_scope(db)
357 }
358
359 pub fn expand(
360 &self, 255 &self,
361 db: &impl HirDatabase, 256 db: &impl HirDatabase,
362 macro_call: InFile<&ast::MacroCall>, 257 macro_call: InFile<&ast::MacroCall>,
363 ) -> Option<Expansion> { 258 ) -> Option<HirFileId> {
364 let macro_call_id = 259 let macro_call_id =
365 macro_call.as_call_id(db, |path| self.resolver.resolve_path_as_macro(db, &path))?; 260 macro_call.as_call_id(db, |path| self.resolver.resolve_path_as_macro(db, &path))?;
366 Some(Expansion { macro_call_id }) 261 Some(macro_call_id.as_file())
367 } 262 }
368} 263}
369 264
@@ -409,6 +304,47 @@ fn scope_for_offset(
409 }) 304 })
410} 305}
411 306
307pub(crate) fn resolve_hir_path(
308 db: &impl HirDatabase,
309 resolver: &Resolver,
310 path: &crate::Path,
311) -> Option<PathResolution> {
312 let types = resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty {
313 TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
314 TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }),
315 TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => PathResolution::Def(Adt::from(it).into()),
316 TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
317 TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
318 TypeNs::BuiltinType(it) => PathResolution::Def(it.into()),
319 TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
320 });
321 let body_owner = resolver.body_owner();
322 let values = resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| {
323 let res = match val {
324 ValueNs::LocalBinding(pat_id) => {
325 let var = Local { parent: body_owner?.into(), pat_id };
326 PathResolution::Local(var)
327 }
328 ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()),
329 ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()),
330 ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()),
331 ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()),
332 ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()),
333 };
334 Some(res)
335 });
336
337 let items = resolver
338 .resolve_module_path_in_items(db, path.mod_path())
339 .take_types()
340 .map(|it| PathResolution::Def(it.into()));
341 types.or(values).or(items).or_else(|| {
342 resolver
343 .resolve_path_as_macro(db, path.mod_path())
344 .map(|def| PathResolution::Macro(def.into()))
345 })
346}
347
412// XXX: during completion, cursor might be outside of any particular 348// XXX: during completion, cursor might be outside of any particular
413// expression. Try to figure out the correct scope... 349// expression. Try to figure out the correct scope...
414fn adjust( 350fn adjust(