diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 71123a6983..92ab0f6922 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -5,18 +5,19 @@ use ra_db::{CrateId, FileId}; use ra_syntax::{ast::self, TreeArc, SyntaxNode}; use crate::{ - Name, Path, PerNs, ScopesWithSyntaxMapping, Ty, HirFileId, + Name, ScopesWithSyntaxMapping, Ty, HirFileId, type_ref::TypeRef, nameres::{ModuleScope, lower::ImportId}, HirDatabase, PersistentHirDatabase, - expr::BodySyntaxMapping, - ty::{InferenceResult}, + expr::{Body, BodySyntaxMapping}, + ty::InferenceResult, adt::{EnumVariantId, StructFieldId, VariantDef}, generics::GenericParams, docs::{Documentation, Docs, docs_from_ast}, module_tree::ModuleId, ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, impl_block::ImplId, + resolve::Resolver, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -174,13 +175,14 @@ impl Module { db.item_map(self.krate)[self.module_id].clone() } - pub fn resolve_path(&self, db: &impl PersistentHirDatabase, path: &Path) -> PerNs { - db.item_map(self.krate).resolve_path(db, *self, path) - } - pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc, Problem)> { self.problems_impl(db) } + + pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + let item_map = db.item_map(self.krate); + Resolver::default().push_module_scope(item_map, *self) + } } impl Docs for Module { @@ -282,6 +284,21 @@ impl Struct { pub fn ty(&self, db: &impl HirDatabase) -> Ty { db.type_for_def((*self).into()) } + + // TODO move to a more general type + /// Builds a resolver for type references inside this struct. + pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + // take the outer scope... + let r = self.module(db).resolver(db); + // ...and add generic params, if present + let p = self.generic_params(db); + let r = if !p.params.is_empty() { + r.push_generic_params_scope(p) + } else { + r + }; + r + } } impl Docs for Struct { @@ -331,6 +348,21 @@ impl Enum { pub fn ty(&self, db: &impl HirDatabase) -> Ty { db.type_for_def((*self).into()) } + + // TODO move to a more general type + /// Builds a resolver for type references inside this struct. + pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + // take the outer scope... + let r = self.module(db).resolver(db); + // ...and add generic params, if present + let p = self.generic_params(db); + let r = if !p.params.is_empty() { + r.push_generic_params_scope(p) + } else { + r + }; + r + } } impl Docs for Enum { @@ -449,6 +481,10 @@ impl Function { db.body_syntax_mapping(*self) } + pub fn body(&self, db: &impl HirDatabase) -> Arc { + db.body_hir(*self) + } + pub fn scopes(&self, db: &impl HirDatabase) -> ScopesWithSyntaxMapping { let scopes = db.expr_scopes(*self); let syntax_mapping = db.body_syntax_mapping(*self); @@ -469,6 +505,24 @@ impl Function { pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc { db.generic_params((*self).into()) } + + // TODO move to a more general type for 'body-having' items + /// Builds a resolver for code inside this item. + pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + // take the outer scope... + let r = self + .impl_block(db) + .map(|ib| ib.resolver(db)) + .unwrap_or_else(|| self.module(db).resolver(db)); + // ...and add generic params, if present + let p = self.generic_params(db); + let r = if !p.params.is_empty() { + r.push_generic_params_scope(p) + } else { + r + }; + r + } } impl Docs for Function { diff --git a/crates/ra_hir/src/code_model_impl/function.rs b/crates/ra_hir/src/code_model_impl/function.rs index 5b0b31b1d5..8326c02c78 100644 --- a/crates/ra_hir/src/code_model_impl/function.rs +++ b/crates/ra_hir/src/code_model_impl/function.rs @@ -5,14 +5,12 @@ use ra_syntax::ast::{self, NameOwner}; use crate::{ HirDatabase, Name, AsName, Function, FnSignature, type_ref::{TypeRef, Mutability}, - expr::Body, PersistentHirDatabase, + PersistentHirDatabase, impl_block::ImplBlock, }; impl Function { - pub(crate) fn body(&self, db: &impl HirDatabase) -> Arc { - db.body_hir(*self) - } + // TODO impl_block should probably also be part of the code model API? /// The containing impl block, if this is a method. pub(crate) fn impl_block(&self, db: &impl HirDatabase) -> Option { diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index f4a9504184..f9f702ae2b 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -10,15 +10,15 @@ use ra_syntax::{ }; use crate::{ - Path, Name, Function, - name::AsName, HirDatabase, + Path, Name, HirDatabase, Function, Resolver, + name::AsName, type_ref::{Mutability, TypeRef}, }; use crate::ty::primitive::{UintTy, UncertainIntTy, UncertainFloatTy}; pub use self::scope::{ExprScopes, ScopesWithSyntaxMapping, ScopeEntryWithSyntax}; -mod scope; +pub(crate) mod scope; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ExprId(RawId); @@ -27,6 +27,9 @@ impl_arena_id!(ExprId); /// The body of an item (function, const etc.). #[derive(Debug, Eq, PartialEq)] pub struct Body { + // TODO: this should be more general, consts & statics also have bodies + /// The Function of the item this body belongs to + owner: Function, exprs: Arena, pats: Arena, /// The patterns for the function's parameters. While the parameter types are @@ -62,6 +65,34 @@ impl Body { pub fn body_expr(&self) -> ExprId { self.body_expr } + + pub fn owner(&self) -> Function { + self.owner + } + + pub fn syntax_mapping(&self, db: &impl HirDatabase) -> Arc { + db.body_syntax_mapping(self.owner) + } +} + +// needs arbitrary_self_types to be a method... or maybe move to the def? +pub fn resolver_for_expr(body: Arc, db: &impl HirDatabase, expr_id: ExprId) -> Resolver { + let scopes = db.expr_scopes(body.owner); + resolver_for_scope(body, db, scopes.scope_for(expr_id)) +} + +pub fn resolver_for_scope( + body: Arc, + db: &impl HirDatabase, + scope_id: Option, +) -> Resolver { + let mut r = body.owner.resolver(db); + let scopes = db.expr_scopes(body.owner); + let scope_chain = scopes.scope_chain_for(scope_id).collect::>(); + for scope in scope_chain.into_iter().rev() { + r = r.push_expr_scope(Arc::clone(&scopes), scope); + } + r } impl Index for Body { @@ -448,23 +479,29 @@ pub(crate) fn body_hir(db: &impl HirDatabase, func: Function) -> Arc { } struct ExprCollector { + owner: Function, exprs: Arena, pats: Arena, expr_syntax_mapping: FxHashMap, expr_syntax_mapping_back: ArenaMap, pat_syntax_mapping: FxHashMap, pat_syntax_mapping_back: ArenaMap, + params: Vec, + body_expr: Option, } impl ExprCollector { - fn new() -> Self { + fn new(owner: Function) -> Self { ExprCollector { + owner, exprs: Arena::default(), pats: Arena::default(), expr_syntax_mapping: FxHashMap::default(), expr_syntax_mapping_back: ArenaMap::default(), pat_syntax_mapping: FxHashMap::default(), pat_syntax_mapping_back: ArenaMap::default(), + params: Vec::new(), + body_expr: None, } } @@ -902,10 +939,7 @@ impl ExprCollector { }); fields.extend(iter); - Pat::Struct { - path: path, - args: fields, - } + Pat::Struct { path, args: fields } } // TODO: implement @@ -923,12 +957,48 @@ impl ExprCollector { } } - fn into_body_syntax_mapping(self, params: Vec, body_expr: ExprId) -> BodySyntaxMapping { + fn collect_fn_body(&mut self, node: &ast::FnDef) { + if let Some(param_list) = node.param_list() { + if let Some(self_param) = param_list.self_param() { + let self_param = SyntaxNodePtr::new( + self_param + .self_kw() + .expect("self param without self keyword") + .syntax(), + ); + let param_pat = self.alloc_pat( + Pat::Bind { + name: Name::self_param(), + mode: BindingAnnotation::Unannotated, + subpat: None, + }, + self_param, + ); + self.params.push(param_pat); + } + + for param in param_list.params() { + let pat = if let Some(pat) = param.pat() { + pat + } else { + continue; + }; + let param_pat = self.collect_pat(pat); + self.params.push(param_pat); + } + }; + + let body = self.collect_block_opt(node.body()); + self.body_expr = Some(body); + } + + fn into_body_syntax_mapping(self) -> BodySyntaxMapping { let body = Body { + owner: self.owner, exprs: self.exprs, pats: self.pats, - params, - body_expr, + params: self.params, + body_expr: self.body_expr.expect("A body should have been collected"), }; BodySyntaxMapping { body: Arc::new(body), @@ -940,49 +1010,18 @@ impl ExprCollector { } } -pub(crate) fn collect_fn_body_syntax(node: &ast::FnDef) -> BodySyntaxMapping { - let mut collector = ExprCollector::new(); - - let params = if let Some(param_list) = node.param_list() { - let mut params = Vec::new(); - - if let Some(self_param) = param_list.self_param() { - let self_param = SyntaxNodePtr::new( - self_param - .self_kw() - .expect("self param without self keyword") - .syntax(), - ); - let param = collector.alloc_pat( - Pat::Bind { - name: Name::self_param(), - mode: BindingAnnotation::Unannotated, - subpat: None, - }, - self_param, - ); - params.push(param); - } - - for param in param_list.params() { - let pat = if let Some(pat) = param.pat() { - pat - } else { - continue; - }; - params.push(collector.collect_pat(pat)); - } - params - } else { - Vec::new() - }; - - let body = collector.collect_block_opt(node.body()); - collector.into_body_syntax_mapping(params, body) -} - pub(crate) fn body_syntax_mapping(db: &impl HirDatabase, func: Function) -> Arc { - let (_, fn_def) = func.source(db); - let body_syntax_mapping = collect_fn_body_syntax(&fn_def); - Arc::new(body_syntax_mapping) + let mut collector = ExprCollector::new(func); + + // TODO: consts, etc. + collector.collect_fn_body(&func.source(db).1); + + Arc::new(collector.into_body_syntax_mapping()) +} + +#[cfg(test)] +pub(crate) fn collect_fn_body_syntax(function: Function, node: &ast::FnDef) -> BodySyntaxMapping { + let mut collector = ExprCollector::new(function); + collector.collect_fn_body(node); + collector.into_body_syntax_mapping() } diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index b7971088d0..9202e36712 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -58,28 +58,19 @@ impl ExprScopes { scopes } + pub fn body(&self) -> Arc { + self.body.clone() + } + pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { &self.scopes[scope].entries } - pub fn scope_chain_for<'a>(&'a self, expr: ExprId) -> impl Iterator + 'a { - generate(self.scope_for(expr), move |&scope| { - self.scopes[scope].parent - }) - } - - pub fn resolve_local_name<'a>( + pub fn scope_chain_for<'a>( &'a self, - context_expr: ExprId, - name: Name, - ) -> Option<&'a ScopeEntry> { - let mut shadowed = FxHashSet::default(); - let ret = self - .scope_chain_for(context_expr) - .flat_map(|scope| self.entries(scope).iter()) - .filter(|entry| shadowed.insert(entry.name())) - .find(|entry| entry.name() == &name); - ret + scope: Option, + ) -> impl Iterator + 'a { + generate(scope, move |&scope| self.scopes[scope].parent) } fn root_scope(&mut self) -> ScopeId { @@ -122,7 +113,7 @@ impl ExprScopes { self.scope_for.insert(node, scope); } - fn scope_for(&self, expr: ExprId) -> Option { + pub fn scope_for(&self, expr: ExprId) -> Option { self.scope_for.get(&expr).map(|&scope| scope) } } @@ -150,18 +141,14 @@ impl ScopeEntryWithSyntax { } impl ScopesWithSyntaxMapping { - pub fn scope_chain<'a>(&'a self, node: &SyntaxNode) -> impl Iterator + 'a { + fn scope_chain<'a>(&'a self, node: &SyntaxNode) -> impl Iterator + 'a { generate(self.scope_for(node), move |&scope| { self.scopes.scopes[scope].parent }) } - pub fn scope_chain_for_offset<'a>( - &'a self, - offset: TextUnit, - ) -> impl Iterator + 'a { - let scope = self - .scopes + pub fn scope_for_offset<'a>(&'a self, offset: TextUnit) -> Option { + self.scopes .scope_for .iter() .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope))) @@ -172,13 +159,12 @@ impl ScopesWithSyntaxMapping { ptr.range().len(), ) }) - .map(|(ptr, scope)| self.adjust(ptr, *scope, offset)); - - generate(scope, move |&scope| self.scopes.scopes[scope].parent) + .map(|(ptr, scope)| self.adjust(ptr, *scope, offset)) } // XXX: during completion, cursor might be outside of any particular // expression. Try to figure out the correct scope... + // TODO: move this to source binder? fn adjust(&self, ptr: SyntaxNodePtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId { let r = ptr.range(); let child_scopes = self @@ -238,7 +224,7 @@ impl ScopesWithSyntaxMapping { .collect() } - fn scope_for(&self, node: &SyntaxNode) -> Option { + pub fn scope_for(&self, node: &SyntaxNode) -> Option { node.ancestors() .map(SyntaxNodePtr::new) .filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr)) @@ -336,6 +322,7 @@ pub struct ReferenceDescriptor { mod tests { use ra_syntax::{SourceFile, algo::find_node_at_offset}; use test_utils::{extract_offset, assert_eq_text}; + use ra_arena::ArenaId; use crate::expr; @@ -354,7 +341,10 @@ mod tests { let file = SourceFile::parse(&code); let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); - let body_hir = expr::collect_fn_body_syntax(fn_def); + let irrelevant_function = Function { + id: crate::ids::FunctionId::from_raw(0.into()), + }; + let body_hir = expr::collect_fn_body_syntax(irrelevant_function, fn_def); let scopes = ExprScopes::new(Arc::clone(body_hir.body())); let scopes = ScopesWithSyntaxMapping { scopes: Arc::new(scopes), @@ -454,7 +444,10 @@ mod tests { let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); - let body_hir = expr::collect_fn_body_syntax(fn_def); + let irrelevant_function = Function { + id: crate::ids::FunctionId::from_raw(0.into()), + }; + let body_hir = expr::collect_fn_body_syntax(irrelevant_function, fn_def); let scopes = ExprScopes::new(Arc::clone(body_hir.body())); let scopes = ScopesWithSyntaxMapping { scopes: Arc::new(scopes), diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index b0bd735bda..a82205f0b3 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -12,6 +12,7 @@ use crate::{db::PersistentHirDatabase, Name, AsName, Function, Struct, Enum, Tra /// Data about a generic parameter (to a function, struct, impl, ...). #[derive(Clone, PartialEq, Eq, Debug)] pub struct GenericParam { + // TODO: give generic params proper IDs pub(crate) idx: u32, pub(crate) name: Name, } diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 36d72b103b..738c58fbe8 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -7,11 +7,13 @@ use ra_syntax::{ ast::{self, AstNode}}; use crate::{ - Const, Type, - Function, HirFileId, - PersistentHirDatabase, + Const, Type, Function, HirFileId, + HirDatabase, PersistentHirDatabase, + ModuleDef, Trait, Resolution, type_ref::TypeRef, ids::LocationCtx, + resolve::Resolver, + ty::Ty, }; use crate::code_model_api::{Module, ModuleSource}; @@ -69,7 +71,11 @@ impl ImplBlock { &self.module_impl_blocks.impls[self.impl_id] } - pub fn target_trait(&self) -> Option<&TypeRef> { + pub fn module(&self) -> Module { + self.module_impl_blocks.module.clone() + } + + pub fn target_trait_ref(&self) -> Option<&TypeRef> { self.impl_data().target_trait() } @@ -77,9 +83,32 @@ impl ImplBlock { self.impl_data().target_type() } + pub fn target_ty(&self, db: &impl HirDatabase) -> Ty { + Ty::from_hir(db, &self.resolver(db), self.target_type()) + } + + pub fn target_trait(&self, db: &impl HirDatabase) -> Option { + if let Some(TypeRef::Path(path)) = self.target_trait_ref() { + let resolver = self.resolver(db); + if let Some(Resolution::Def(ModuleDef::Trait(tr))) = + resolver.resolve_path(db, path).take_types() + { + return Some(tr); + } + } + None + } + pub fn items(&self) -> &[ImplItem] { self.impl_data().items() } + + pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + let r = self.module().resolver(db); + // TODO: add generics + let r = r.push_impl_block_scope(self.clone()); + r + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -162,25 +191,24 @@ impl_arena_id!(ImplId); /// we don't need to do the second step again. #[derive(Debug, PartialEq, Eq)] pub struct ModuleImplBlocks { + module: Module, pub(crate) impls: Arena, impls_by_def: FxHashMap, } impl ModuleImplBlocks { - fn new() -> Self { - ModuleImplBlocks { - impls: Arena::default(), - impls_by_def: FxHashMap::default(), - } - } - fn collect( - &mut self, db: &impl PersistentHirDatabase, module: Module, source_map: &mut ImplSourceMap, - ) { - let (file_id, module_source) = module.definition_source(db); + ) -> Self { + let mut m = ModuleImplBlocks { + module, + impls: Arena::default(), + impls_by_def: FxHashMap::default(), + }; + + let (file_id, module_source) = m.module.definition_source(db); let file_id: HirFileId = file_id.into(); let node = match &module_source { ModuleSource::SourceFile(node) => node.syntax(), @@ -191,14 +219,16 @@ impl ModuleImplBlocks { }; for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) { - let impl_block = ImplData::from_ast(db, file_id, module, impl_block_ast); - let id = self.impls.alloc(impl_block); - for &impl_item in &self.impls[id].items { - self.impls_by_def.insert(impl_item, id); + let impl_block = ImplData::from_ast(db, file_id, m.module, impl_block_ast); + let id = m.impls.alloc(impl_block); + for &impl_item in &m.impls[id].items { + m.impls_by_def.insert(impl_item, id); } source_map.insert(id, impl_block_ast); } + + m } } @@ -208,8 +238,7 @@ pub(crate) fn impls_in_module_with_source_map_query( ) -> (Arc, Arc) { let mut source_map = ImplSourceMap::default(); - let mut result = ModuleImplBlocks::new(); - result.collect(db, module, &mut source_map); + let result = ModuleImplBlocks::collect(db, module, &mut source_map); (Arc::new(result), Arc::new(source_map)) } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 905c53c7dd..54da555985 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -36,6 +36,7 @@ mod impl_block; mod expr; mod generics; mod docs; +mod resolve; mod code_model_api; mod code_model_impl; @@ -54,12 +55,13 @@ pub use self::{ name::Name, ids::{HirFileId, MacroCallId, MacroCallLoc, HirInterner}, macros::{MacroDef, MacroInput, MacroExpansion}, - nameres::{ItemMap, PerNs, Namespace, Resolution}, + nameres::{ItemMap, PerNs, Namespace}, ty::Ty, impl_block::{ImplBlock, ImplItem}, docs::{Docs, Documentation}, adt::AdtDef, expr::{ExprScopes, ScopesWithSyntaxMapping}, + resolve::{Resolver, Resolution}, }; pub use self::code_model_api::{ diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index f8627acbe0..7ec6512b64 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -24,13 +24,13 @@ use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ Module, ModuleDef, - Path, PathKind, Crate, - Name, PersistentHirDatabase, + Path, PathKind, PersistentHirDatabase, + Crate, Name, module_tree::{ModuleId, ModuleTree}, nameres::lower::{ImportId, LoweredModule, ImportData}, }; -/// `ItemMap` is the result of name resolution. It contains, for each +/// `ItemMap` is the result of module name resolution. It contains, for each /// module, the set of visible items. #[derive(Default, Debug, PartialEq, Eq)] pub struct ItemMap { @@ -46,7 +46,7 @@ impl std::ops::Index for ItemMap { #[derive(Debug, Default, PartialEq, Eq, Clone)] pub struct ModuleScope { - items: FxHashMap, + pub(crate) items: FxHashMap, } impl ModuleScope { @@ -80,6 +80,15 @@ pub struct PerNs { pub values: Option, } +impl Default for PerNs { + fn default() -> Self { + PerNs { + types: None, + values: None, + } + } +} + impl PerNs { pub fn none() -> PerNs { PerNs { @@ -113,6 +122,10 @@ impl PerNs { self.types.is_none() && self.values.is_none() } + pub fn is_both(&self) -> bool { + self.types.is_some() && self.values.is_some() + } + pub fn take(self, namespace: Namespace) -> Option { match namespace { Namespace::Types => self.types, @@ -139,6 +152,13 @@ impl PerNs { } } + pub fn combine(self, other: PerNs) -> PerNs { + PerNs { + types: self.types.or(other.types), + values: self.values.or(other.values), + } + } + pub fn and_then(self, f: impl Fn(T) -> Option) -> PerNs { PerNs { types: self.types.and_then(&f), @@ -402,10 +422,11 @@ impl ItemMap { if module.krate != original_module.krate { let path = Path { segments: path.segments[i..].iter().cloned().collect(), - kind: PathKind::Crate, + kind: PathKind::Self_, }; log::debug!("resolving {:?} in other crate", path); - let def = module.resolve_path(db, &path); + let item_map = db.item_map(module.krate); + let def = item_map.resolve_path(db, *module, &path); return (def, ReachedFixedPoint::Yes); } diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 1ce7bd146d..0e0683db77 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs @@ -5,11 +5,12 @@ use relative_path::RelativePath; use test_utils::{assert_eq_text, covers}; use crate::{ - ItemMap, Resolution, + ItemMap, PersistentHirDatabase, mock::MockDatabase, module_tree::ModuleId, }; +use super::Resolution; fn item_map(fixture: &str) -> (Arc, ModuleId) { let (db, pos) = MockDatabase::with_position(fixture); diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs new file mode 100644 index 0000000000..6c87d0df72 --- /dev/null +++ b/crates/ra_hir/src/resolve.rs @@ -0,0 +1,226 @@ +//! Name resolution. +use std::sync::Arc; + +use rustc_hash::FxHashMap; + +use crate::{ + ModuleDef, Module, + db::HirDatabase, + name::{Name, KnownName}, + nameres::{PerNs, ItemMap}, + generics::GenericParams, + expr::{scope::{ExprScopes, ScopeId}, PatId, Body}, + impl_block::ImplBlock, + path::Path, +}; + +#[derive(Debug, Clone, Default)] +pub struct Resolver { + scopes: Vec, +} + +// TODO how to store these best +#[derive(Debug, Clone)] +pub(crate) struct ModuleItemMap { + item_map: Arc, + module: Module, +} + +#[derive(Debug, Clone)] +pub(crate) struct ExprScope { + expr_scopes: Arc, + scope_id: ScopeId, +} + +#[derive(Debug, Clone)] +pub(crate) enum Scope { + /// All the items and imported names of a module + ModuleScope(ModuleItemMap), + /// Brings the generic parameters of an item into scope + GenericParams(Arc), + /// Brings `Self` into scope + ImplBlockScope(ImplBlock), + /// Local bindings + ExprScope(ExprScope), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Resolution { + /// An item + Def(ModuleDef), + /// A local binding (only value namespace) + LocalBinding(PatId), + /// A generic parameter + GenericParam(u32), + SelfType(ImplBlock), +} + +impl Resolver { + pub fn resolve_name(&self, name: &Name) -> PerNs { + let mut resolution = PerNs::none(); + for scope in self.scopes.iter().rev() { + resolution = resolution.combine(scope.resolve_name(name)); + if resolution.is_both() { + return resolution; + } + } + resolution + } + + pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs { + if let Some(name) = path.as_ident() { + self.resolve_name(name) + } else if path.is_self() { + self.resolve_name(&Name::self_param()) + } else { + let (item_map, module) = match self.module() { + Some(m) => m, + _ => return PerNs::none(), + }; + let module_res = item_map.resolve_path(db, module, path); + module_res.map(|def| Resolution::Def(def)) + } + } + + pub fn all_names(&self) -> FxHashMap> { + let mut names = FxHashMap::default(); + for scope in self.scopes.iter().rev() { + scope.collect_names(&mut |name, res| { + let current: &mut PerNs = names.entry(name).or_default(); + if current.types.is_none() { + current.types = res.types; + } + if current.values.is_none() { + current.values = res.values; + } + }); + } + names + } + + fn module(&self) -> Option<(&ItemMap, Module)> { + self.scopes.iter().rev().find_map(|scope| match scope { + Scope::ModuleScope(m) => Some((&*m.item_map, m.module.clone())), + + _ => None, + }) + } + + /// The body from which any `LocalBinding` resolutions in this resolver come. + pub fn body(&self) -> Option> { + self.scopes.iter().rev().find_map(|scope| match scope { + Scope::ExprScope(expr_scope) => Some(expr_scope.expr_scopes.body()), + _ => None, + }) + } +} + +impl Resolver { + pub(crate) fn push_scope(mut self, scope: Scope) -> Resolver { + self.scopes.push(scope); + self + } + + pub(crate) fn push_generic_params_scope(self, params: Arc) -> Resolver { + self.push_scope(Scope::GenericParams(params)) + } + + pub(crate) fn push_impl_block_scope(self, impl_block: ImplBlock) -> Resolver { + self.push_scope(Scope::ImplBlockScope(impl_block)) + } + + pub(crate) fn push_module_scope(self, item_map: Arc, module: Module) -> Resolver { + self.push_scope(Scope::ModuleScope(ModuleItemMap { item_map, module })) + } + + pub(crate) fn push_expr_scope( + self, + expr_scopes: Arc, + scope_id: ScopeId, + ) -> Resolver { + self.push_scope(Scope::ExprScope(ExprScope { + expr_scopes, + scope_id, + })) + } +} + +impl Scope { + fn resolve_name(&self, name: &Name) -> PerNs { + match self { + Scope::ModuleScope(m) => { + if let Some(KnownName::SelfParam) = name.as_known_name() { + PerNs::types(Resolution::Def(m.module.into())) + } else { + match m.item_map[m.module.module_id].get(name) { + Some(res) => res.def.map(Resolution::Def), + None => PerNs::none(), + } + } + } + Scope::GenericParams(gp) => match gp.find_by_name(name) { + Some(gp) => PerNs::types(Resolution::GenericParam(gp.idx)), + None => PerNs::none(), + }, + Scope::ImplBlockScope(i) => { + if name.as_known_name() == Some(KnownName::SelfType) { + PerNs::types(Resolution::SelfType(i.clone())) + } else { + PerNs::none() + } + } + Scope::ExprScope(e) => { + let entry = e + .expr_scopes + .entries(e.scope_id) + .iter() + .find(|entry| entry.name() == name); + match entry { + Some(e) => PerNs::values(Resolution::LocalBinding(e.pat())), + None => PerNs::none(), + } + } + } + } + + fn collect_names(&self, f: &mut FnMut(Name, PerNs)) { + match self { + Scope::ModuleScope(m) => { + // TODO: should we provide `self` here? + // f( + // Name::self_param(), + // PerNs::types(Resolution::Def { + // def: m.module.into(), + // }), + // ); + m.item_map[m.module.module_id] + .entries() + .for_each(|(name, res)| { + f(name.clone(), res.def.map(Resolution::Def)); + }) + } + Scope::GenericParams(gp) => { + for param in &gp.params { + f( + param.name.clone(), + PerNs::types(Resolution::GenericParam(param.idx)), + ) + } + } + Scope::ImplBlockScope(i) => { + f( + Name::self_type(), + PerNs::types(Resolution::SelfType(i.clone())), + ); + } + Scope::ExprScope(e) => { + e.expr_scopes.entries(e.scope_id).iter().for_each(|e| { + f( + e.name().clone(), + PerNs::values(Resolution::LocalBinding(e.pat())), + ); + }); + } + } + } +} diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a1b94ed9c9..59f7822774 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -9,13 +9,14 @@ use ra_db::{FileId, FilePosition}; use ra_syntax::{ SmolStr, TextRange, SyntaxNode, ast::{self, AstNode, NameOwner}, - algo::find_node_at_offset, + algo::{find_node_at_offset, find_leaf_at_offset}, }; use crate::{ HirDatabase, Function, ModuleDef, Struct, Enum, - AsName, Module, HirFileId, Crate, Trait, + AsName, Module, HirFileId, Crate, Trait, Resolver, ids::{LocationCtx, SourceFileItemId}, + expr }; /// Locates the module by `FileId`. Picks topmost module in the file. @@ -201,3 +202,67 @@ pub fn macro_symbols(db: &impl HirDatabase, file_id: FileId) -> Vec<(SmolStr, Te res } + +pub fn resolver_for_position(db: &impl HirDatabase, position: FilePosition) -> Resolver { + let file_id = position.file_id; + let file = db.parse(file_id); + find_leaf_at_offset(file.syntax(), position.offset) + .find_map(|node| { + node.ancestors().find_map(|node| { + if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() { + if let Some(func) = function_from_child_node(db, file_id, node) { + let scopes = func.scopes(db); + let scope = scopes.scope_for_offset(position.offset); + Some(expr::resolver_for_scope(func.body(db), db, scope)) + } else { + // TODO const/static/array length + None + } + } else if let Some(module) = ast::Module::cast(node) { + Some(module_from_declaration(db, file_id, module)?.resolver(db)) + } else if let Some(_) = ast::SourceFile::cast(node) { + Some(module_from_source(db, file_id.into(), None)?.resolver(db)) + } else if let Some(s) = ast::StructDef::cast(node) { + let module = module_from_child_node(db, file_id, s.syntax())?; + Some(struct_from_module(db, module, s).resolver(db)) + } else if let Some(e) = ast::EnumDef::cast(node) { + let module = module_from_child_node(db, file_id, e.syntax())?; + Some(enum_from_module(db, module, e).resolver(db)) + } else { + // TODO add missing cases + None + } + }) + }) + .unwrap_or_default() +} + +pub fn resolver_for_node(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> Resolver { + node.ancestors() + .find_map(|node| { + if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() { + if let Some(func) = function_from_child_node(db, file_id, node) { + let scopes = func.scopes(db); + let scope = scopes.scope_for(&node); + Some(expr::resolver_for_scope(func.body(db), db, scope)) + } else { + // TODO const/static/array length + None + } + } else if let Some(module) = ast::Module::cast(node) { + Some(module_from_declaration(db, file_id, module)?.resolver(db)) + } else if let Some(_) = ast::SourceFile::cast(node) { + Some(module_from_source(db, file_id.into(), None)?.resolver(db)) + } else if let Some(s) = ast::StructDef::cast(node) { + let module = module_from_child_node(db, file_id, s.syntax())?; + Some(struct_from_module(db, module, s).resolver(db)) + } else if let Some(e) = ast::EnumDef::cast(node) { + let module = module_from_child_node(db, file_id, e.syntax())?; + Some(enum_from_module(db, module, e).resolver(db)) + } else { + // TODO add missing cases + None + } + }) + .unwrap_or_default() +} diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d3e31981a8..cc5afad753 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -33,15 +33,16 @@ use rustc_hash::FxHashMap; use test_utils::tested_by; use crate::{ - Module, Function, Struct, StructField, Enum, EnumVariant, Path, Name, ImplBlock, - FnSignature, ExprScopes, ModuleDef, AdtDef, + Function, Struct, StructField, Enum, EnumVariant, Path, Name, + FnSignature, ModuleDef, AdtDef, HirDatabase, type_ref::{TypeRef, Mutability}, name::KnownName, - expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat}, + expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self}, generics::GenericParams, path::GenericArg, adt::VariantDef, + resolve::{Resolver, Resolution}, }; /// The ID of a type variable. @@ -300,47 +301,38 @@ pub struct FnSig { } impl Ty { - pub(crate) fn from_hir( - db: &impl HirDatabase, - // TODO: the next three parameters basically describe the scope for name - // resolution; this should be refactored into something like a general - // resolver architecture - module: &Module, - impl_block: Option<&ImplBlock>, - generics: &GenericParams, - type_ref: &TypeRef, - ) -> Self { + pub(crate) fn from_hir(db: &impl HirDatabase, resolver: &Resolver, type_ref: &TypeRef) -> Self { match type_ref { TypeRef::Never => Ty::Never, TypeRef::Tuple(inner) => { let inner_tys = inner .iter() - .map(|tr| Ty::from_hir(db, module, impl_block, generics, tr)) + .map(|tr| Ty::from_hir(db, resolver, tr)) .collect::>(); Ty::Tuple(inner_tys.into()) } - TypeRef::Path(path) => Ty::from_hir_path(db, module, impl_block, generics, path), + TypeRef::Path(path) => Ty::from_hir_path(db, resolver, path), TypeRef::RawPtr(inner, mutability) => { - let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner); + let inner_ty = Ty::from_hir(db, resolver, inner); Ty::RawPtr(Arc::new(inner_ty), *mutability) } TypeRef::Array(inner) => { - let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner); + let inner_ty = Ty::from_hir(db, resolver, inner); Ty::Array(Arc::new(inner_ty)) } TypeRef::Slice(inner) => { - let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner); + let inner_ty = Ty::from_hir(db, resolver, inner); Ty::Slice(Arc::new(inner_ty)) } TypeRef::Reference(inner, mutability) => { - let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner); + let inner_ty = Ty::from_hir(db, resolver, inner); Ty::Ref(Arc::new(inner_ty), *mutability) } TypeRef::Placeholder => Ty::Unknown, TypeRef::Fn(params) => { let mut inner_tys = params .iter() - .map(|tr| Ty::from_hir(db, module, impl_block, generics, tr)) + .map(|tr| Ty::from_hir(db, resolver, tr)) .collect::>(); let return_ty = inner_tys .pop() @@ -355,40 +347,13 @@ impl Ty { } } - pub(crate) fn from_hir_opt( - db: &impl HirDatabase, - module: &Module, - impl_block: Option<&ImplBlock>, - generics: &GenericParams, - type_ref: Option<&TypeRef>, - ) -> Self { - type_ref.map_or(Ty::Unknown, |t| { - Ty::from_hir(db, module, impl_block, generics, t) - }) - } - - pub(crate) fn from_hir_path( - db: &impl HirDatabase, - module: &Module, - impl_block: Option<&ImplBlock>, - generics: &GenericParams, - path: &Path, - ) -> Self { + pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self { if let Some(name) = path.as_ident() { + // TODO handle primitive type names in resolver as well? if let Some(int_ty) = primitive::UncertainIntTy::from_name(name) { return Ty::Int(int_ty); } else if let Some(float_ty) = primitive::UncertainFloatTy::from_name(name) { return Ty::Float(float_ty); - } else if name.as_known_name() == Some(KnownName::SelfType) { - // TODO pass the impl block's generics? - let generics = &GenericParams::default(); - return Ty::from_hir_opt( - db, - module, - None, - generics, - impl_block.map(|i| i.target_type()), - ); } else if let Some(known) = name.as_known_name() { match known { KnownName::Bool => return Ty::Bool, @@ -396,25 +361,40 @@ impl Ty { KnownName::Str => return Ty::Str, _ => {} } - } else if let Some(generic_param) = generics.find_by_name(&name) { - return Ty::Param { - idx: generic_param.idx, - name: generic_param.name.clone(), - }; } } - // Resolve in module (in type namespace) - let typable: TypableDef = match module - .resolve_path(db, path) - .take_types() - .and_then(|it| it.into()) - { + // Resolve the path (in type namespace) + let resolution = resolver.resolve_path(db, path).take_types(); + + let def = match resolution { + Some(Resolution::Def(def)) => def, + Some(Resolution::LocalBinding(..)) => { + // this should never happen + panic!("path resolved to local binding in type ns"); + } + Some(Resolution::GenericParam(idx)) => { + return Ty::Param { + idx, + // TODO: maybe return name in resolution? + name: path + .as_ident() + .expect("generic param should be single-segment path") + .clone(), + }; + } + Some(Resolution::SelfType(impl_block)) => { + return impl_block.target_ty(db); + } + None => return Ty::Unknown, + }; + + let typable: TypableDef = match def.into() { None => return Ty::Unknown, Some(it) => it, }; let ty = db.type_for_def(typable); - let substs = Ty::substs_from_path(db, module, impl_block, generics, path, typable); + let substs = Ty::substs_from_path(db, resolver, path, typable); ty.apply_substs(substs) } @@ -422,10 +402,7 @@ impl Ty { /// `create_substs_for_ast_path` and `def_to_ty` in rustc. fn substs_from_path( db: &impl HirDatabase, - // the scope of the segment... - module: &Module, - impl_block: Option<&ImplBlock>, - outer_generics: &GenericParams, + resolver: &Resolver, path: &Path, resolved: TypableDef, ) -> Substs { @@ -462,7 +439,7 @@ impl Ty { for arg in generic_args.args.iter().take(param_count) { match arg { GenericArg::Type(type_ref) => { - let ty = Ty::from_hir(db, module, impl_block, outer_generics, type_ref); + let ty = Ty::from_hir(db, resolver, type_ref); substs.push(ty); } } @@ -666,24 +643,17 @@ impl fmt::Display for Ty { /// function body. fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty { let signature = def.signature(db); - let module = def.module(db); - let impl_block = def.impl_block(db); + let resolver = def.resolver(db); let generics = def.generic_params(db); + let name = def.name(db); let input = signature .params() .iter() - .map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), &generics, tr)) + .map(|tr| Ty::from_hir(db, &resolver, tr)) .collect::>(); - let output = Ty::from_hir( - db, - &module, - impl_block.as_ref(), - &generics, - signature.ret_type(), - ); + let output = Ty::from_hir(db, &resolver, signature.ret_type()); let sig = Arc::new(FnSig { input, output }); let substs = make_substs(&generics); - let name = def.name(db); Ty::FnDef { def, sig, @@ -764,13 +734,13 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def: TypableDef) -> Ty { pub(super) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty { let parent_def = field.parent_def(db); - let (generics, module) = match parent_def { - VariantDef::Struct(it) => (it.generic_params(db), it.module(db)), - VariantDef::EnumVariant(it) => (it.parent_enum(db).generic_params(db), it.module(db)), + let resolver = match parent_def { + VariantDef::Struct(it) => it.resolver(db), + VariantDef::EnumVariant(it) => it.parent_enum(db).resolver(db), }; let var_data = parent_def.variant_data(db); let type_ref = &var_data.fields().unwrap()[field.id].type_ref; - Ty::from_hir(db, &module, None, &generics, type_ref) + Ty::from_hir(db, &resolver, type_ref) } /// The result of type inference: A mapping from expressions and patterns to types. @@ -814,9 +784,7 @@ impl Index for InferenceResult { struct InferenceContext<'a, D: HirDatabase> { db: &'a D, body: Arc, - scopes: Arc, - module: Module, - impl_block: Option, + resolver: Resolver, var_unification_table: InPlaceUnificationTable, method_resolutions: FxHashMap, field_resolutions: FxHashMap, @@ -905,13 +873,7 @@ fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { } impl<'a, D: HirDatabase> InferenceContext<'a, D> { - fn new( - db: &'a D, - body: Arc, - scopes: Arc, - module: Module, - impl_block: Option, - ) -> Self { + fn new(db: &'a D, body: Arc, resolver: Resolver) -> Self { InferenceContext { method_resolutions: FxHashMap::default(), field_resolutions: FxHashMap::default(), @@ -921,9 +883,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { return_ty: Ty::Unknown, // set in collect_fn_signature db, body, - scopes, - module, - impl_block, + resolver, } } @@ -940,8 +900,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { *ty = resolved; } InferenceResult { - method_resolutions: mem::replace(&mut self.method_resolutions, Default::default()), - field_resolutions: mem::replace(&mut self.field_resolutions, Default::default()), + method_resolutions: self.method_resolutions, + field_resolutions: self.field_resolutions, type_of_expr: expr_types, type_of_pat: pat_types, } @@ -964,13 +924,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } fn make_ty(&mut self, type_ref: &TypeRef) -> Ty { - // TODO provide generics of function - let generics = GenericParams::default(); let ty = Ty::from_hir( self.db, - &self.module, - self.impl_block.as_ref(), - &generics, + // TODO use right resolver for block + &self.resolver, type_ref, ); let ty = self.insert_type_vars(ty); @@ -1147,38 +1104,31 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { }) } - fn infer_path_expr(&mut self, expr: ExprId, path: &Path) -> Option { - if path.is_ident() || path.is_self() { - // resolve locally - let name = path.as_ident().cloned().unwrap_or_else(Name::self_param); - if let Some(scope_entry) = self.scopes.resolve_local_name(expr, name) { - let ty = self.type_of_pat.get(scope_entry.pat())?; + fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path) -> Option { + let resolved = resolver.resolve_path(self.db, &path).take_values()?; + match resolved { + Resolution::Def(def) => { + let typable: Option = def.into(); + let typable = typable?; + let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); + let ty = self.db.type_for_def(typable).apply_substs(substs); + let ty = self.insert_type_vars(ty); + Some(ty) + } + Resolution::LocalBinding(pat) => { + let ty = self.type_of_pat.get(pat)?; let ty = self.resolve_ty_as_possible(&mut vec![], ty.clone()); - return Some(ty); - }; - }; - - // resolve in module - let typable: Option = self - .module - .resolve_path(self.db, &path) - .take_values()? - .into(); - let typable = typable?; - let ty = self.db.type_for_def(typable); - let generics = GenericParams::default(); - let substs = Ty::substs_from_path( - self.db, - &self.module, - self.impl_block.as_ref(), - &generics, - path, - typable, - ); - let ty = ty.apply_substs(substs); - let ty = self.insert_type_vars(ty); - - Some(ty) + Some(ty) + } + Resolution::GenericParam(..) => { + // generic params can't refer to values... yet + None + } + Resolution::SelfType(_) => { + log::error!("path expr {:?} resolved to Self type in values ns", path); + None + } + } } fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option) { @@ -1186,26 +1136,30 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Some(path) => path, None => return (Ty::Unknown, None), }; - let typable: Option = self - .module - .resolve_path(self.db, &path) - .take_types() - .and_then(|it| it.into()); + let resolver = &self.resolver; + let typable: Option = match resolver.resolve_path(self.db, &path).take_types() { + Some(Resolution::Def(def)) => def.into(), + Some(Resolution::LocalBinding(..)) => { + // this cannot happen + log::error!("path resolved to local binding in type ns"); + return (Ty::Unknown, None); + } + Some(Resolution::GenericParam(..)) => { + // generic params can't be used in struct literals + return (Ty::Unknown, None); + } + Some(Resolution::SelfType(..)) => { + // TODO this is allowed in an impl for a struct, handle this + return (Ty::Unknown, None); + } + None => return (Ty::Unknown, None), + }; let def = match typable { None => return (Ty::Unknown, None), Some(it) => it, }; // TODO remove the duplication between here and `Ty::from_path`? - // TODO provide generics of function - let generics = GenericParams::default(); - let substs = Ty::substs_from_path( - self.db, - &self.module, - self.impl_block.as_ref(), - &generics, - path, - def, - ); + let substs = Ty::substs_from_path(self.db, resolver, path, def); match def { TypableDef::Struct(s) => { let ty = type_for_struct(self.db, s); @@ -1303,12 +1257,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { path: ref p, args: ref fields, } => self.infer_struct_pat(p.as_ref(), fields, expected), - Pat::Path(path) => self - .module - .resolve_path(self.db, &path) - .take_values() - .and_then(|module_def| module_def.into()) - .map_or(Ty::Unknown, |resolved| self.db.type_for_def(resolved)), + Pat::Path(path) => { + // TODO use correct resolver for the surrounding expression + let resolver = self.resolver.clone(); + self.infer_path_expr(&resolver, &path) + .unwrap_or(Ty::Unknown) + } Pat::Bind { mode, name: _name, @@ -1496,7 +1450,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { expected.ty } - Expr::Path(p) => self.infer_path_expr(tgt_expr, p).unwrap_or(Ty::Unknown), + Expr::Path(p) => { + // TODO this could be more efficient... + let resolver = expr::resolver_for_expr(self.body.clone(), self.db, tgt_expr); + self.infer_path_expr(&resolver, p).unwrap_or(Ty::Unknown) + } Expr::Continue => Ty::Never, Expr::Break { expr } => { if let Some(expr) = expr { @@ -1730,10 +1688,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { pub fn infer(db: &impl HirDatabase, func: Function) -> Arc { db.check_canceled(); let body = func.body(db); - let scopes = db.expr_scopes(func); - let module = func.module(db); - let impl_block = func.impl_block(db); - let mut ctx = InferenceContext::new(db, body, scopes, module, impl_block); + let resolver = func.resolver(db); + let mut ctx = InferenceContext::new(db, body, resolver); let signature = func.signature(db); ctx.collect_fn_signature(&signature); diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index e857d6856e..2282286b0b 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -7,12 +7,10 @@ use std::sync::Arc; use rustc_hash::FxHashMap; use crate::{ - HirDatabase, module_tree::ModuleId, Module, ModuleDef, Crate, Name, Function, Trait, + HirDatabase, module_tree::ModuleId, Module, Crate, Name, Function, Trait, ids::TraitId, impl_block::{ImplId, ImplBlock, ImplItem}, - generics::GenericParams, ty::{AdtDef, Ty}, - type_ref::TypeRef, }; /// This is used as a key for indexing impls. @@ -85,17 +83,10 @@ impl CrateImplBlocks { fn collect_recursive(&mut self, db: &impl HirDatabase, module: &Module) { let module_impl_blocks = db.impls_in_module(module.clone()); - for (impl_id, impl_data) in module_impl_blocks.impls.iter() { + for (impl_id, _) in module_impl_blocks.impls.iter() { let impl_block = ImplBlock::from_id(Arc::clone(&module_impl_blocks), impl_id); - // TODO provide generics of impl - let generics = GenericParams::default(); - let target_ty = Ty::from_hir( - db, - &module, - Some(&impl_block), - &generics, - impl_data.target_type(), - ); + + let target_ty = impl_block.target_ty(db); if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { self.impls @@ -104,14 +95,11 @@ impl CrateImplBlocks { .push((module.module_id, impl_id)); } - if let Some(TypeRef::Path(path)) = impl_data.target_trait() { - let perns = module.resolve_path(db, path); - if let Some(ModuleDef::Trait(tr)) = perns.take_types() { - self.impls_by_trait - .entry(tr.id) - .or_insert_with(Vec::new) - .push((module.module_id, impl_id)); - } + if let Some(tr) = impl_block.target_trait(db) { + self.impls_by_trait + .entry(tr.id) + .or_insert_with(Vec::new) + .push((module.module_id, impl_id)); } } diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_function_generics.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_function_generics.snap index 8ff6e55a6b..91c48897cf 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_function_generics.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_function_generics.snap @@ -1,12 +1,12 @@ --- -created: "2019-01-26T18:16:16.530712344+00:00" +created: "2019-01-27T14:52:29.934503829+00:00" creator: insta@0.5.2 expression: "&result" source: crates/ra_hir/src/ty/tests.rs --- -[10; 11) 't': [unknown] -[21; 26) '{ t }': [unknown] -[23; 24) 't': [unknown] +[10; 11) 't': T +[21; 26) '{ t }': T +[23; 24) 't': T [38; 98) '{ ...(1); }': () [44; 46) 'id': fn id(T) -> T [44; 52) 'id(1u32)': u32 diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap index f21bffa754..626f312525 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_generic_chain.snap @@ -1,5 +1,5 @@ --- -created: "2019-01-26T17:46:03.866825843+00:00" +created: "2019-01-27T14:52:29.938713255+00:00" creator: insta@0.5.2 expression: "&result" source: crates/ra_hir/src/ty/tests.rs @@ -8,9 +8,9 @@ source: crates/ra_hir/src/ty/tests.rs [65; 87) '{ ... }': [unknown] [75; 79) 'self': A<[unknown]> [75; 81) 'self.x': [unknown] -[99; 100) 't': [unknown] -[110; 115) '{ t }': [unknown] -[112; 113) 't': [unknown] +[99; 100) 't': T +[110; 115) '{ t }': T +[112; 113) 't': T [135; 261) '{ ....x() }': i128 [146; 147) 'x': i32 [150; 151) '1': i32 diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_type_param.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_type_param.snap index a993232642..216d1e41f1 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_type_param.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_type_param.snap @@ -1,15 +1,15 @@ --- -created: "2019-01-27T16:54:18.368427685+00:00" +created: "2019-01-27T20:38:32.153717698+00:00" creator: insta@0.5.2 expression: "&result" source: crates/ra_hir/src/ty/tests.rs --- -[10; 11) 'x': [unknown] -[21; 30) '{ x }': [unknown] -[27; 28) 'x': [unknown] -[44; 45) 'x': &[unknown] -[56; 65) '{ x }': &[unknown] -[62; 63) 'x': &[unknown] +[10; 11) 'x': T +[21; 30) '{ x }': T +[27; 28) 'x': T +[44; 45) 'x': &T +[56; 65) '{ x }': &T +[62; 63) 'x': &T [77; 157) '{ ...(1); }': () [87; 88) 'y': u32 [91; 96) '10u32': u32 diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index b33ddcde56..0b9948d4b4 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs @@ -1,21 +1,21 @@ use join_to_string::join; +use hir::{Docs, Resolution}; + use crate::{ completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}, }; -use hir::Docs; - pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { - let (path, module) = match (&ctx.path_prefix, &ctx.module) { - (Some(path), Some(module)) => (path.clone(), module), + let path = match &ctx.path_prefix { + Some(path) => path.clone(), _ => return, }; - let def_id = match module.resolve_path(ctx.db, &path).take_types() { - Some(it) => it, - None => return, + let def = match ctx.resolver.resolve_path(ctx.db, &path).take_types() { + Some(Resolution::Def(def)) => def, + _ => return, }; - match def_id { + match def { hir::ModuleDef::Module(module) => { let module_scope = module.scope(ctx.db); for (name, res) in module_scope.entries() { @@ -24,7 +24,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { ctx.source_range(), name.to_string(), ) - .from_resolution(ctx, res) + .from_resolution(ctx, &res.def.map(hir::Resolution::Def)) .add_to(acc); } } @@ -65,6 +65,17 @@ mod tests { check_completion(code, expected_completions, CompletionKind::Reference); } + #[test] + #[ignore] // should not complete foo, which currently doesn't work + fn dont_complete_current_use() { + check_reference_completion( + "dont_complete_current_use", + r" + use self::foo<|>; + ", + ); + } + #[test] fn completes_mod_with_docs() { check_reference_completion( diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index f837bb1db2..44514ab2b2 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -1,63 +1,20 @@ -use rustc_hash::FxHashSet; -use ra_syntax::ast::AstNode; -use crate::completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}; +use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext}; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.is_trivial_path { return; } - let module = match &ctx.module { - Some(it) => it, - None => return, - }; - if let Some(function) = &ctx.function { - let scopes = function.scopes(ctx.db); - complete_fn(acc, &scopes, ctx); - } + let names = ctx.resolver.all_names(); - let module_scope = module.scope(ctx.db); - module_scope - .entries() - .filter(|(_name, res)| { - // For cases like `use self::foo<|>` don't suggest foo itself. - match res.import { - None => true, - Some(import) => { - let source = module.import_source(ctx.db, import); - !source.syntax().range().is_subrange(&ctx.leaf.range()) - } - } - }) - .for_each(|(name, res)| { - CompletionItem::new( - CompletionKind::Reference, - ctx.source_range(), - name.to_string(), - ) - .from_resolution(ctx, res) - .add_to(acc) - }); -} - -fn complete_fn( - acc: &mut Completions, - scopes: &hir::ScopesWithSyntaxMapping, - ctx: &CompletionContext, -) { - let mut shadowed = FxHashSet::default(); - scopes - .scope_chain_for_offset(ctx.offset) - .flat_map(|scope| scopes.scopes.entries(scope).iter()) - .filter(|entry| shadowed.insert(entry.name())) - .for_each(|entry| { - CompletionItem::new( - CompletionKind::Reference, - ctx.source_range(), - entry.name().to_string(), - ) - .kind(CompletionItemKind::Binding) - .add_to(acc) - }); + names.into_iter().for_each(|(name, res)| { + CompletionItem::new( + CompletionKind::Reference, + ctx.source_range(), + name.to_string(), + ) + .from_resolution(ctx, &res) + .add_to(acc) + }); } #[cfg(test)] @@ -115,6 +72,30 @@ mod tests { ); } + #[test] + fn completes_generic_params() { + check_reference_completion( + "generic_params", + r" + fn quux() { + <|> + } + ", + ); + } + + #[test] + fn completes_generic_params_in_struct() { + check_reference_completion( + "generic_params_in_struct", + r" + struct X { + x: <|> + } + ", + ); + } + #[test] fn completes_module_items() { check_reference_completion( @@ -174,5 +155,4 @@ mod tests { fn completes_self_in_methods() { check_reference_completion("self_in_methods", r"impl S { fn foo(&self) { <|> } }") } - } diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index 578af6e5b3..5d1851da64 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -5,7 +5,7 @@ use ra_syntax::{ algo::{find_leaf_at_offset, find_covering_node, find_node_at_offset}, SyntaxKind::*, }; -use hir::source_binder; +use hir::{source_binder, Resolver}; use crate::{db, FilePosition}; @@ -16,6 +16,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) db: &'a db::RootDatabase, pub(super) offset: TextUnit, pub(super) leaf: &'a SyntaxNode, + pub(super) resolver: Resolver, pub(super) module: Option, pub(super) function: Option, pub(super) function_syntax: Option<&'a ast::FnDef>, @@ -42,12 +43,14 @@ impl<'a> CompletionContext<'a> { original_file: &'a SourceFile, position: FilePosition, ) -> Option> { + let resolver = source_binder::resolver_for_position(db, position); let module = source_binder::module_from_position(db, position); let leaf = find_leaf_at_offset(original_file.syntax(), position.offset).left_biased()?; let mut ctx = CompletionContext { db, leaf, offset: position.offset, + resolver, module, function: None, function_syntax: None, diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs index d3bc148944..bada6a33bf 100644 --- a/crates/ra_ide_api/src/completion/completion_item.rs +++ b/crates/ra_ide_api/src/completion/completion_item.rs @@ -1,5 +1,7 @@ -use hir::{Docs, Documentation}; -use ra_syntax::TextRange; +use hir::{Docs, Documentation, PerNs, Resolution}; +use ra_syntax::{ + TextRange, +}; use ra_text_edit::TextEdit; use test_utils::tested_by; @@ -48,6 +50,7 @@ pub enum CompletionItemKind { Trait, TypeAlias, Method, + TypeParam, } #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -207,23 +210,34 @@ impl Builder { pub(super) fn from_resolution( mut self, ctx: &CompletionContext, - resolution: &hir::Resolution, + resolution: &PerNs, ) -> Builder { - let def = resolution.def.take_types().or(resolution.def.take_values()); + use hir::ModuleDef::*; + + let def = resolution + .as_ref() + .take_types() + .or(resolution.as_ref().take_values()); let def = match def { None => return self, Some(it) => it, }; let (kind, docs) = match def { - hir::ModuleDef::Module(it) => (CompletionItemKind::Module, it.docs(ctx.db)), - hir::ModuleDef::Function(func) => return self.from_function(ctx, func), - hir::ModuleDef::Struct(it) => (CompletionItemKind::Struct, it.docs(ctx.db)), - hir::ModuleDef::Enum(it) => (CompletionItemKind::Enum, it.docs(ctx.db)), - hir::ModuleDef::EnumVariant(it) => (CompletionItemKind::EnumVariant, it.docs(ctx.db)), - hir::ModuleDef::Const(it) => (CompletionItemKind::Const, it.docs(ctx.db)), - hir::ModuleDef::Static(it) => (CompletionItemKind::Static, it.docs(ctx.db)), - hir::ModuleDef::Trait(it) => (CompletionItemKind::Trait, it.docs(ctx.db)), - hir::ModuleDef::Type(it) => (CompletionItemKind::TypeAlias, it.docs(ctx.db)), + Resolution::Def(Module(it)) => (CompletionItemKind::Module, it.docs(ctx.db)), + Resolution::Def(Function(func)) => return self.from_function(ctx, *func), + Resolution::Def(Struct(it)) => (CompletionItemKind::Struct, it.docs(ctx.db)), + Resolution::Def(Enum(it)) => (CompletionItemKind::Enum, it.docs(ctx.db)), + Resolution::Def(EnumVariant(it)) => (CompletionItemKind::EnumVariant, it.docs(ctx.db)), + Resolution::Def(Const(it)) => (CompletionItemKind::Const, it.docs(ctx.db)), + Resolution::Def(Static(it)) => (CompletionItemKind::Static, it.docs(ctx.db)), + Resolution::Def(Trait(it)) => (CompletionItemKind::Trait, it.docs(ctx.db)), + Resolution::Def(Type(it)) => (CompletionItemKind::TypeAlias, it.docs(ctx.db)), + Resolution::GenericParam(..) => (CompletionItemKind::TypeParam, None), + Resolution::LocalBinding(..) => (CompletionItemKind::Binding, None), + Resolution::SelfType(..) => ( + CompletionItemKind::TypeParam, // (does this need its own kind?) + None, + ), }; self.kind = Some(kind); self.documentation = docs; diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__generic_params.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__generic_params.snap new file mode 100644 index 0000000000..71cb55a5bc --- /dev/null +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__generic_params.snap @@ -0,0 +1,40 @@ +--- +created: "2019-02-01T22:20:40.580128393+00:00" +creator: insta@0.5.3 +expression: kind_completions +source: crates/ra_ide_api/src/completion/completion_item.rs +--- +[ + CompletionItem { + completion_kind: Reference, + label: "T", + kind: Some( + TypeParam + ), + detail: None, + documentation: None, + lookup: None, + insert_text: None, + insert_text_format: PlainText, + source_range: [44; 44), + text_edit: None + }, + CompletionItem { + completion_kind: Reference, + label: "quux", + kind: Some( + Function + ), + detail: Some( + "fn quux()" + ), + documentation: None, + lookup: None, + insert_text: Some( + "quux()$0" + ), + insert_text_format: Snippet, + source_range: [44; 44), + text_edit: None + } +] diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__generic_params_in_struct.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__generic_params_in_struct.snap new file mode 100644 index 0000000000..a35c0cd13a --- /dev/null +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__generic_params_in_struct.snap @@ -0,0 +1,36 @@ +--- +created: "2019-02-01T22:23:21.508620224+00:00" +creator: insta@0.5.3 +expression: kind_completions +source: crates/ra_ide_api/src/completion/completion_item.rs +--- +[ + CompletionItem { + completion_kind: Reference, + label: "T", + kind: Some( + TypeParam + ), + detail: None, + documentation: None, + lookup: None, + insert_text: None, + insert_text_format: PlainText, + source_range: [46; 46), + text_edit: None + }, + CompletionItem { + completion_kind: Reference, + label: "X", + kind: Some( + Struct + ), + detail: None, + documentation: None, + lookup: None, + insert_text: None, + insert_text_format: PlainText, + source_range: [46; 46), + text_edit: None + } +] diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap index 6a49e325c1..ba1d4abbd5 100644 --- a/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap @@ -1,10 +1,24 @@ --- -created: "2019-01-23T05:27:32.422259+00:00" -creator: insta@0.4.0 +created: "2019-01-27T20:17:10.051725945+00:00" +creator: insta@0.5.2 expression: kind_completions source: crates/ra_ide_api/src/completion/completion_item.rs --- [ + CompletionItem { + completion_kind: Reference, + label: "Self", + kind: Some( + TypeParam + ), + detail: None, + documentation: None, + lookup: None, + insert_text: None, + insert_text_format: PlainText, + source_range: [25; 25), + text_edit: None + }, CompletionItem { completion_kind: Reference, label: "self", diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 2a20c20ee9..88efcea2a5 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -4,6 +4,7 @@ use ra_syntax::{ algo::find_node_at_offset, }; use test_utils::tested_by; +use hir::Resolution; use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; @@ -48,14 +49,7 @@ pub(crate) fn reference_definition( if let Some(function) = hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax()) { - let scope = function.scopes(db); - // First try to resolve the symbol locally - if let Some(entry) = scope.resolve_local_name(name_ref) { - let nav = NavigationTarget::from_scope_entry(file_id, &entry); - return Exact(nav); - }; - - // Next check if it is a method + // Check if it is a method if let Some(method_call) = name_ref .syntax() .parent() @@ -86,19 +80,37 @@ pub(crate) fn reference_definition( }; } } - // Then try module name resolution - if let Some(module) = hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax()) + // Try name resolution + let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax()); + if let Some(path) = name_ref + .syntax() + .ancestors() + .find_map(ast::Path::cast) + .and_then(hir::Path::from_ast) { - if let Some(path) = name_ref - .syntax() - .ancestors() - .find_map(ast::Path::cast) - .and_then(hir::Path::from_ast) - { - let resolved = module.resolve_path(db, &path); - if let Some(def_id) = resolved.take_types().or(resolved.take_values()) { - return Exact(NavigationTarget::from_def(db, def_id)); + let resolved = resolver.resolve_path(db, &path); + match resolved.clone().take_types().or(resolved.take_values()) { + Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)), + Some(Resolution::LocalBinding(pat)) => { + let body = resolver.body().expect("no body for local binding"); + let syntax_mapping = body.syntax_mapping(db); + let ptr = syntax_mapping + .pat_syntax(pat) + .expect("pattern not found in syntax mapping"); + let name = path + .as_ident() + .cloned() + .expect("local binding from a multi-segment path"); + let nav = NavigationTarget::from_scope_entry(file_id, name, ptr); + return Exact(nav); } + Some(Resolution::GenericParam(..)) => { + // TODO go to the generic param def + } + Some(Resolution::SelfType(_impl_block)) => { + // TODO go to the implemented type + } + None => {} } } // If that fails try the index based approach. diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index 5ccb5cc2e3..617908aedf 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs @@ -1,9 +1,9 @@ use ra_db::FileId; use ra_syntax::{ - SyntaxNode, AstNode, SmolStr, TextRange, ast, + SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, ast, SyntaxKind::{self, NAME}, }; -use hir::{ModuleSource, FieldSource}; +use hir::{ModuleSource, FieldSource, Name}; use crate::{FileSymbol, db::RootDatabase}; @@ -58,12 +58,13 @@ impl NavigationTarget { pub(crate) fn from_scope_entry( file_id: FileId, - entry: &hir::ScopeEntryWithSyntax, + name: Name, + ptr: SyntaxNodePtr, ) -> NavigationTarget { NavigationTarget { file_id, - name: entry.name().to_string().into(), - full_range: entry.ptr().range(), + name: name.to_string().into(), + full_range: ptr.range(), focus_range: None, kind: NAME, } diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index c033ecdeaa..17fa073406 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -70,6 +70,7 @@ impl Conv for CompletionItemKind { CompletionItemKind::Const => Constant, CompletionItemKind::Static => Value, CompletionItemKind::Method => Method, + CompletionItemKind::TypeParam => TypeParameter, } } }