diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs index f24835d177..031d8b98f2 100644 --- a/crates/ra_analysis/src/completion/complete_dot.rs +++ b/crates/ra_analysis/src/completion/complete_dot.rs @@ -72,6 +72,21 @@ mod tests { ); } + #[test] + fn test_struct_field_completion_self() { + check_ref_completion( + r" + struct A { the_field: u32 } + impl A { + fn foo(self) { + self.<|> + } + } + ", + r#"the_field"#, + ); + } + #[test] fn test_no_struct_field_completion_for_method_call() { check_ref_completion( diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index d7740f0c4e..5422a400b6 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs @@ -105,6 +105,7 @@ salsa::database_storage! { fn type_for_field() for hir::db::TypeForFieldQuery; fn struct_data() for hir::db::StructDataQuery; fn enum_data() for hir::db::EnumDataQuery; + fn impls_in_module() for hir::db::ImplsInModuleQuery; } } } diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs index 9605294046..846c76cfe7 100644 --- a/crates/ra_analysis/src/mock_analysis.rs +++ b/crates/ra_analysis/src/mock_analysis.rs @@ -4,7 +4,7 @@ use relative_path::RelativePathBuf; use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; use ra_db::mock::FileMap; -use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, FileRange, SourceRootId}; +use crate::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, FilePosition, FileRange, SourceRootId}; /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis /// from a set of in-memory files. @@ -87,12 +87,17 @@ impl MockAnalysis { let source_root = SourceRootId(0); let mut change = AnalysisChange::new(); change.add_root(source_root, true); + let mut crate_graph = CrateGraph::default(); for (path, contents) in self.files.into_iter() { assert!(path.starts_with('/')); let path = RelativePathBuf::from_path(&path[1..]).unwrap(); let file_id = file_map.add(path.clone()); + if path == "/lib.rs" || path == "/main.rs" { + crate_graph.add_crate_root(file_id); + } change.add_file(source_root, file_id, path, Arc::new(contents)); } + change.set_crate_graph(crate_graph); // change.set_file_resolver(Arc::new(file_map)); host.apply_change(change); host diff --git a/crates/ra_analysis/tests/test/main.rs b/crates/ra_analysis/tests/test/main.rs index 859778024a..beeae1e196 100644 --- a/crates/ra_analysis/tests/test/main.rs +++ b/crates/ra_analysis/tests/test/main.rs @@ -138,14 +138,14 @@ fn test_resolve_parent_module_for_inline() { fn test_resolve_crate_root() { let mock = MockAnalysis::with_files( " - //- /lib.rs + //- /bar.rs mod foo; - //- /foo.rs + //- /bar/foo.rs // emtpy <|> ", ); - let root_file = mock.id_of("/lib.rs"); - let mod_file = mock.id_of("/foo.rs"); + let root_file = mock.id_of("/bar.rs"); + let mod_file = mock.id_of("/bar/foo.rs"); let mut host = mock.analysis_host(); assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 73a4cdc5c8..58296fc6f3 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -13,6 +13,7 @@ use crate::{ nameres::{ItemMap, InputModuleItems}}, ty::{InferenceResult, Ty}, adt::{StructData, EnumData}, + impl_block::ModuleImplBlocks, }; salsa::query_group! { @@ -87,6 +88,11 @@ pub trait HirDatabase: SyntaxDatabase type ModuleTreeQuery; use fn crate::module::imp::module_tree; } + + fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable> { + type ImplsInModuleQuery; + use fn crate::impl_block::impls_in_module; + } } } diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs index 5a44132fcc..75ef308ae4 100644 --- a/crates/ra_hir/src/function.rs +++ b/crates/ra_hir/src/function.rs @@ -11,11 +11,11 @@ use ra_syntax::{ ast::{self, AstNode, DocCommentsOwner, NameOwner}, }; -use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module}; +use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock}; pub use self::scope::FnScopes; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Function { def_id: DefId, } @@ -25,6 +25,10 @@ impl Function { Function { def_id } } + pub fn def_id(&self) -> DefId { + self.def_id + } + pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode { let def_loc = self.def_id.loc(db); assert!(def_loc.kind == DefKind::Function); @@ -48,6 +52,15 @@ impl Function { pub fn module(&self, db: &impl HirDatabase) -> Cancelable { self.def_id.module(db) } + + pub fn krate(&self, db: &impl HirDatabase) -> Cancelable> { + self.def_id.krate(db) + } + + /// The containing impl block, if this is a method. + pub fn impl_block(&self, db: &impl HirDatabase) -> Cancelable> { + self.def_id.impl_block(db) + } } #[derive(Debug, Clone)] diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 66adacc7d4..4d6378e028 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -2,7 +2,7 @@ use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId}; use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; use ra_arena::{Arena, RawId, impl_arena_id}; -use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum}; +use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, ImplBlock, Crate}; /// hir makes a heavy use of ids: integer (u32) handlers to various things. You /// can think of id as a pointer (but without a lifetime) or a file descriptor @@ -177,6 +177,18 @@ impl DefId { let loc = self.loc(db); Module::new(db, loc.source_root_id, loc.module_id) } + + /// Returns the containing crate. + pub fn krate(&self, db: &impl HirDatabase) -> Cancelable> { + Ok(self.module(db)?.krate(db)) + } + + /// Returns the containing impl block, if this is an impl item. + pub fn impl_block(self, db: &impl HirDatabase) -> Cancelable> { + let loc = self.loc(db); + let module_impls = db.impls_in_module(loc.source_root_id, loc.module_id)?; + Ok(ImplBlock::containing(module_impls, self)) + } } impl DefLoc { diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs new file mode 100644 index 0000000000..01afa84c49 --- /dev/null +++ b/crates/ra_hir/src/impl_block.rs @@ -0,0 +1,180 @@ +use std::sync::Arc; +use rustc_hash::FxHashMap; + +use ra_arena::{Arena, RawId, impl_arena_id}; +use ra_syntax::ast::{self, AstNode}; +use ra_db::{LocationIntener, Cancelable, SourceRootId}; + +use crate::{ + DefId, DefLoc, DefKind, SourceItemId, SourceFileItems, + Module, Function, + db::HirDatabase, + type_ref::TypeRef, + module::{ModuleSourceNode, ModuleId}, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImplBlock { + module_impl_blocks: Arc, + impl_id: ImplId, +} + +impl ImplBlock { + pub(crate) fn containing( + module_impl_blocks: Arc, + def_id: DefId, + ) -> Option { + let impl_id = *module_impl_blocks.impls_by_def.get(&def_id)?; + Some(ImplBlock { + module_impl_blocks, + impl_id, + }) + } + + fn impl_data(&self) -> &ImplData { + &self.module_impl_blocks.impls[self.impl_id] + } + + pub fn target_trait(&self) -> Option<&TypeRef> { + self.impl_data().target_trait.as_ref() + } + + pub fn target_type(&self) -> &TypeRef { + &self.impl_data().target_type + } + + pub fn items(&self) -> &[ImplItem] { + &self.impl_data().items + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImplData { + target_trait: Option, + target_type: TypeRef, + items: Vec, +} + +impl ImplData { + pub(crate) fn from_ast( + db: &impl AsRef>, + file_items: &SourceFileItems, + module: &Module, + node: ast::ImplBlock, + ) -> Self { + let target_trait = node.target_type().map(TypeRef::from_ast); + let target_type = TypeRef::from_ast_opt(node.target_type()); + let file_id = module.source().file_id(); + let items = if let Some(item_list) = node.item_list() { + item_list + .impl_items() + .map(|item_node| { + let kind = match item_node { + ast::ImplItem::FnDef(..) => DefKind::Function, + ast::ImplItem::ConstDef(..) => DefKind::Item, + ast::ImplItem::TypeDef(..) => DefKind::Item, + }; + let item_id = file_items.id_of_unchecked(item_node.syntax()); + let def_loc = DefLoc { + kind, + source_root_id: module.source_root_id, + module_id: module.module_id, + source_item_id: SourceItemId { + file_id, + item_id: Some(item_id), + }, + }; + let def_id = def_loc.id(db); + match item_node { + ast::ImplItem::FnDef(..) => ImplItem::Method(Function::new(def_id)), + ast::ImplItem::ConstDef(..) => ImplItem::Const(def_id), + ast::ImplItem::TypeDef(..) => ImplItem::Type(def_id), + } + }) + .collect() + } else { + Vec::new() + }; + ImplData { + target_trait, + target_type, + items, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ImplItem { + Method(Function), + // these don't have their own types yet + Const(DefId), + Type(DefId), + // Existential +} + +impl ImplItem { + pub fn def_id(&self) -> DefId { + match self { + ImplItem::Method(f) => f.def_id(), + ImplItem::Const(def_id) => *def_id, + ImplItem::Type(def_id) => *def_id, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ImplId(pub RawId); +impl_arena_id!(ImplId); + +/// Collection of impl blocks is a two-step process: First we collect the blocks +/// per-module; then we build an index of all impl blocks in the crate. This +/// way, we avoid having to do this process for the whole crate whenever someone +/// types in any file; as long as the impl blocks in the file don't change, we +/// don't need to do the second step again. +/// +/// (The second step does not yet exist currently.) +#[derive(Debug, PartialEq, Eq)] +pub struct ModuleImplBlocks { + 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 HirDatabase, module: Module) -> Cancelable<()> { + let module_source_node = module.source().resolve(db); + let node = match &module_source_node { + ModuleSourceNode::SourceFile(node) => node.borrowed().syntax(), + ModuleSourceNode::Module(node) => node.borrowed().syntax(), + }; + + let source_file_items = db.file_items(module.source().file_id()); + + for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) { + let impl_block = ImplData::from_ast(db, &source_file_items, &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.def_id(), id); + } + } + + Ok(()) + } +} + +pub(crate) fn impls_in_module( + db: &impl HirDatabase, + source_root_id: SourceRootId, + module_id: ModuleId, +) -> Cancelable> { + let mut result = ModuleImplBlocks::new(); + let module = Module::new(db, source_root_id, module_id)?; + result.collect(db, module)?; + Ok(Arc::new(result)) +} diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs index a0821d15dd..5194e280b7 100644 --- a/crates/ra_hir/src/krate.rs +++ b/crates/ra_hir/src/krate.rs @@ -5,7 +5,7 @@ use crate::{HirDatabase, Module, Name, AsName, HirFileId}; /// hir::Crate describes a single crate. It's the main inteface with which /// crate's dependencies interact. Mostly, it should be just a proxy for the /// root module. -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Crate { crate_id: CrateId, } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 344b543b6e..2abcec441b 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -31,6 +31,7 @@ mod function; mod adt; mod type_ref; mod ty; +mod impl_block; use crate::{ db::HirDatabase, @@ -48,6 +49,7 @@ pub use self::{ function::{Function, FnScopes}, adt::{Struct, Enum}, ty::Ty, + impl_block::{ImplBlock, ImplItem}, }; pub use self::function::FnSignatureInfo; diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 89b18194ac..a9db932ff0 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -30,6 +30,10 @@ impl MockDatabase { let file_id = db.add_file(&mut source_root, "/main.rs", text); db.query_mut(ra_db::SourceRootQuery) .set(WORKSPACE, Arc::new(source_root.clone())); + + let mut crate_graph = CrateGraph::default(); + crate_graph.add_crate_root(file_id); + db.set_crate_graph(crate_graph); (db, source_root, file_id) } @@ -203,6 +207,7 @@ salsa::database_storage! { fn type_for_field() for db::TypeForFieldQuery; fn struct_data() for db::StructDataQuery; fn enum_data() for db::EnumDataQuery; + fn impls_in_module() for db::ImplsInModuleQuery; } } } diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index c70dc54dd9..b9821115ce 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs @@ -71,6 +71,21 @@ impl Module { }) } + /// Returns an iterator of all children of this module. + pub fn children<'a>(&'a self) -> impl Iterator + 'a { + self.module_id + .children(&self.tree) + .map(move |(name, module_id)| { + ( + name, + Module { + module_id, + ..self.clone() + }, + ) + }) + } + /// Returns the crate this module is part of. pub fn krate(&self, db: &impl HirDatabase) -> Option { let root_id = self.module_id.crate_root(&self.tree); diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 51e8b3da86..017caf442e 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -51,6 +51,7 @@ impl Name { "u128" => KnownName::U128, "f32" => KnownName::F32, "f64" => KnownName::F64, + "Self" => KnownName::Self_, _ => return None, }; Some(name) @@ -84,7 +85,7 @@ impl AsName for ra_db::Dependency { // const ISIZE: Name = Name::new("isize") // ``` // but const-fn is not that powerful yet. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub(crate) enum KnownName { Isize, I8, @@ -102,4 +103,6 @@ pub(crate) enum KnownName { F32, F64, + + Self_, } diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 93f7203fe2..9fdfa0d131 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -70,6 +70,11 @@ impl Path { self.kind == PathKind::Plain && self.segments.len() == 1 } + /// `true` if this path is just a standalone `self` + pub fn is_self(&self) -> bool { + self.kind == PathKind::Self_ && self.segments.len() == 0 + } + /// If this path is a single identifier, like `foo`, return its name. pub fn as_ident(&self) -> Option<&Name> { if self.kind != PathKind::Plain || self.segments.len() > 1 { diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 719b3f7cd7..e33762e0dd 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -31,9 +31,10 @@ use ra_syntax::{ }; use crate::{ - Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, + Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock, db::HirDatabase, type_ref::{TypeRef, Mutability}, + name::KnownName, }; /// The ID of a type variable. @@ -235,6 +236,7 @@ impl Ty { pub(crate) fn from_hir( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, type_ref: &TypeRef, ) -> Cancelable { Ok(match type_ref { @@ -242,29 +244,29 @@ impl Ty { TypeRef::Tuple(inner) => { let inner_tys = inner .iter() - .map(|tr| Ty::from_hir(db, module, tr)) + .map(|tr| Ty::from_hir(db, module, impl_block, tr)) .collect::>>()?; Ty::Tuple(inner_tys.into()) } - TypeRef::Path(path) => Ty::from_hir_path(db, module, path)?, + TypeRef::Path(path) => Ty::from_hir_path(db, module, impl_block, path)?, TypeRef::RawPtr(inner, mutability) => { - let inner_ty = Ty::from_hir(db, module, inner)?; + let inner_ty = Ty::from_hir(db, module, impl_block, inner)?; Ty::RawPtr(Arc::new(inner_ty), *mutability) } TypeRef::Array(_inner) => Ty::Unknown, // TODO TypeRef::Slice(inner) => { - let inner_ty = Ty::from_hir(db, module, inner)?; + let inner_ty = Ty::from_hir(db, module, impl_block, inner)?; Ty::Slice(Arc::new(inner_ty)) } TypeRef::Reference(inner, mutability) => { - let inner_ty = Ty::from_hir(db, module, inner)?; + let inner_ty = Ty::from_hir(db, module, impl_block, 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, tr)) + .map(|tr| Ty::from_hir(db, module, impl_block, tr)) .collect::>>()?; let return_ty = inner_tys .pop() @@ -279,9 +281,21 @@ impl Ty { }) } + pub(crate) fn from_hir_opt( + db: &impl HirDatabase, + module: &Module, + impl_block: Option<&ImplBlock>, + type_ref: Option<&TypeRef>, + ) -> Cancelable { + type_ref + .map(|t| Ty::from_hir(db, module, impl_block, t)) + .unwrap_or(Ok(Ty::Unknown)) + } + pub(crate) fn from_hir_path( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, path: &Path, ) -> Cancelable { if let Some(name) = path.as_ident() { @@ -291,6 +305,8 @@ impl Ty { return Ok(Ty::Uint(uint_ty)); } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { return Ok(Ty::Float(float_ty)); + } else if name.as_known_name() == Some(KnownName::Self_) { + return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); } } @@ -308,18 +324,20 @@ impl Ty { pub(crate) fn from_ast_opt( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, node: Option, ) -> Cancelable { - node.map(|n| Ty::from_ast(db, module, n)) + node.map(|n| Ty::from_ast(db, module, impl_block, n)) .unwrap_or(Ok(Ty::Unknown)) } pub(crate) fn from_ast( db: &impl HirDatabase, module: &Module, + impl_block: Option<&ImplBlock>, node: ast::TypeRef, ) -> Cancelable { - Ty::from_hir(db, module, &TypeRef::from_ast(node)) + Ty::from_hir(db, module, impl_block, &TypeRef::from_ast(node)) } pub fn unit() -> Self { @@ -402,18 +420,19 @@ impl fmt::Display for Ty { fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { let syntax = f.syntax(db); let module = f.module(db)?; + let impl_block = f.impl_block(db)?; let node = syntax.borrowed(); // TODO we ignore type parameters for now let input = node .param_list() .map(|pl| { pl.params() - .map(|p| Ty::from_ast_opt(db, &module, p.type_ref())) + .map(|p| Ty::from_ast_opt(db, &module, impl_block.as_ref(), p.type_ref())) .collect() }) .unwrap_or_else(|| Ok(Vec::new()))?; let output = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) { - Ty::from_ast(db, &module, type_ref)? + Ty::from_ast(db, &module, impl_block.as_ref(), type_ref)? } else { Ty::unit() }; @@ -467,12 +486,13 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) ), }; let module = def_id.module(db)?; + let impl_block = def_id.impl_block(db)?; let type_ref = if let Some(tr) = variant_data.get_field_type_ref(&field) { tr } else { return Ok(Ty::Unknown); }; - Ty::from_hir(db, &module, &type_ref) + Ty::from_hir(db, &module, impl_block.as_ref(), &type_ref) } /// The result of type inference: A mapping from expressions and patterns to types. @@ -496,19 +516,32 @@ impl InferenceResult { struct InferenceContext<'a, D: HirDatabase> { db: &'a D, scopes: Arc, + /// The self param for the current method, if it exists. + self_param: Option, module: Module, + impl_block: Option, var_unification_table: InPlaceUnificationTable, type_of: FxHashMap, + /// The return type of the function being inferred. + return_ty: Ty, } impl<'a, D: HirDatabase> InferenceContext<'a, D> { - fn new(db: &'a D, scopes: Arc, module: Module) -> Self { + fn new( + db: &'a D, + scopes: Arc, + module: Module, + impl_block: Option, + ) -> Self { InferenceContext { type_of: FxHashMap::default(), var_unification_table: InPlaceUnificationTable::new(), + self_param: None, // set during parameter typing + return_ty: Ty::Unknown, // set in collect_fn_signature db, scopes, module, + impl_block, } } @@ -525,6 +558,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.type_of.insert(LocalSyntaxPtr::new(node), ty); } + fn make_ty(&self, type_ref: &TypeRef) -> Cancelable { + Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), type_ref) + } + + fn make_ty_opt(&self, type_ref: Option<&TypeRef>) -> Cancelable { + Ty::from_hir_opt(self.db, &self.module, self.impl_block.as_ref(), type_ref) + } + fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { match (ty1, ty2) { (Ty::Unknown, ..) => true, @@ -628,6 +669,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { let ty = self.resolve_ty_as_possible(ty.clone()); return Ok(Some(ty)); }; + } else if path.is_self() { + // resolve `self` param + let self_param = ctry!(self.self_param); + let ty = ctry!(self.type_of.get(&self_param)); + let ty = self.resolve_ty_as_possible(ty.clone()); + return Ok(Some(ty)); }; // resolve in module @@ -826,7 +873,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } ast::Expr::CastExpr(e) => { let _inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; - let cast_ty = Ty::from_ast_opt(self.db, &self.module, e.type_ref())?; + let cast_ty = Ty::from_ast_opt( + self.db, + &self.module, + self.impl_block.as_ref(), + e.type_ref(), + )?; let cast_ty = self.insert_type_vars(cast_ty); // TODO do the coercion... cast_ty @@ -880,7 +932,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { for stmt in node.statements() { match stmt { ast::Stmt::LetStmt(stmt) => { - let decl_ty = Ty::from_ast_opt(self.db, &self.module, stmt.type_ref())?; + let decl_ty = Ty::from_ast_opt( + self.db, + &self.module, + self.impl_block.as_ref(), + stmt.type_ref(), + )?; let decl_ty = self.insert_type_vars(decl_ty); let ty = if let Some(expr) = stmt.initializer() { let expr_ty = self.infer_expr(expr, &Expectation::has_type(decl_ty))?; @@ -906,46 +963,71 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { self.write_ty(node.syntax(), ty.clone()); Ok(ty) } + + fn collect_fn_signature(&mut self, node: ast::FnDef) -> Cancelable<()> { + if let Some(param_list) = node.param_list() { + if let Some(self_param) = param_list.self_param() { + let self_type = if let Some(type_ref) = self_param.type_ref() { + let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; + self.insert_type_vars(ty) + } else { + // TODO this should be handled by desugaring during HIR conversion + let ty = self.make_ty_opt(self.impl_block.as_ref().map(|i| i.target_type()))?; + let ty = match self_param.flavor() { + ast::SelfParamFlavor::Owned => ty, + ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared), + ast::SelfParamFlavor::MutRef => Ty::Ref(Arc::new(ty), Mutability::Mut), + }; + self.insert_type_vars(ty) + }; + if let Some(self_kw) = self_param.self_kw() { + let self_param = LocalSyntaxPtr::new(self_kw.syntax()); + self.self_param = Some(self_param); + self.type_of.insert(self_param, self_type); + } + } + for param in param_list.params() { + let pat = if let Some(pat) = param.pat() { + pat + } else { + continue; + }; + let ty = if let Some(type_ref) = param.type_ref() { + let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; + self.insert_type_vars(ty) + } else { + // missing type annotation + self.new_type_var() + }; + self.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); + } + } + + self.return_ty = if let Some(type_ref) = node.ret_type().and_then(|n| n.type_ref()) { + let ty = self.make_ty(&TypeRef::from_ast(type_ref))?; + self.insert_type_vars(ty) + } else { + Ty::unit() + }; + + Ok(()) + } } pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable> { let function = Function::new(def_id); // TODO: consts also need inference let scopes = function.scopes(db); let module = function.module(db)?; - let mut ctx = InferenceContext::new(db, scopes, module); + let impl_block = function.impl_block(db)?; + let mut ctx = InferenceContext::new(db, scopes, module, impl_block); let syntax = function.syntax(db); let node = syntax.borrowed(); - if let Some(param_list) = node.param_list() { - for param in param_list.params() { - let pat = if let Some(pat) = param.pat() { - pat - } else { - continue; - }; - if let Some(type_ref) = param.type_ref() { - let ty = Ty::from_ast(db, &ctx.module, type_ref)?; - let ty = ctx.insert_type_vars(ty); - ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); - } else { - // TODO self param - let type_var = ctx.new_type_var(); - ctx.type_of - .insert(LocalSyntaxPtr::new(pat.syntax()), type_var); - }; - } - } - - let ret_ty = if let Some(type_ref) = node.ret_type().and_then(|n| n.type_ref()) { - let ty = Ty::from_ast(db, &ctx.module, type_ref)?; - ctx.insert_type_vars(ty) - } else { - Ty::unit() - }; + ctx.collect_fn_signature(node)?; if let Some(block) = node.body() { - ctx.infer_block(block, &Expectation::has_type(ret_ty))?; + ctx.infer_block(block, &Expectation::has_type(ctx.return_ty.clone()))?; } Ok(Arc::new(ctx.resolve_all())) diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 93bf431c4d..fb53fcf0be 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -134,6 +134,25 @@ fn test() -> &mut &f64 { ); } +#[test] +fn infer_self() { + check_inference( + r#" +struct S; + +impl S { + fn test(&self) { + self; + } + fn test2(self: &Self) { + self; + } +} +"#, + "0007_self.txt", + ); +} + fn infer(content: &str) -> String { let (db, _, file_id) = MockDatabase::with_single_file(content); let source_file = db.source_file(file_id); diff --git a/crates/ra_hir/src/ty/tests/data/0007_self.txt b/crates/ra_hir/src/ty/tests/data/0007_self.txt new file mode 100644 index 0000000000..db4ba17d07 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/0007_self.txt @@ -0,0 +1,6 @@ +[50; 54) 'self': &S +[34; 38) 'self': &S +[40; 61) '{ ... }': () +[88; 109) '{ ... }': () +[98; 102) 'self': &S +[75; 79) 'self': &S diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 7f986d3224..2a3bd27e25 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -482,6 +482,37 @@ impl<'a> PrefixExpr<'a> { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum SelfParamFlavor { + /// self + Owned, + /// &self + Ref, + /// &mut self + MutRef, +} + +impl<'a> SelfParam<'a> { + pub fn flavor(&self) -> SelfParamFlavor { + let borrowed = self.syntax().children().any(|n| n.kind() == AMP); + if borrowed { + // check for a `mut` coming after the & -- `mut &self` != `&mut self` + if self + .syntax() + .children() + .skip_while(|n| n.kind() != AMP) + .any(|n| n.kind() == MUT_KW) + { + SelfParamFlavor::MutRef + } else { + SelfParamFlavor::Ref + } + } else { + SelfParamFlavor::Owned + } + } +} + #[test] fn test_doc_comment_of_items() { let file = SourceFileNode::parse( diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index c1c63f555a..7df6a9c466 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -1442,7 +1442,39 @@ impl> ImplBlockNode { } -impl<'a> ImplBlock<'a> {} +impl<'a> ImplBlock<'a> { + pub fn item_list(self) -> Option> { + super::child_opt(self) + } +} + +// ImplItem +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ImplItem<'a> { + FnDef(FnDef<'a>), + TypeDef(TypeDef<'a>), + ConstDef(ConstDef<'a>), +} + +impl<'a> AstNode<'a> for ImplItem<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + FN_DEF => Some(ImplItem::FnDef(FnDef { syntax })), + TYPE_DEF => Some(ImplItem::TypeDef(TypeDef { syntax })), + CONST_DEF => Some(ImplItem::ConstDef(ConstDef { syntax })), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { + match self { + ImplItem::FnDef(inner) => inner.syntax(), + ImplItem::TypeDef(inner) => inner.syntax(), + ImplItem::ConstDef(inner) => inner.syntax(), + } + } +} + +impl<'a> ImplItem<'a> {} // ImplTraitType #[derive(Debug, Clone, Copy,)] @@ -1555,7 +1587,11 @@ impl> ItemListNode { impl<'a> ast::FnDefOwner<'a> for ItemList<'a> {} impl<'a> ast::ModuleItemOwner<'a> for ItemList<'a> {} -impl<'a> ItemList<'a> {} +impl<'a> ItemList<'a> { + pub fn impl_items(self) -> impl Iterator> + 'a { + super::children(self) + } +} // Label #[derive(Debug, Clone, Copy,)] @@ -3452,6 +3488,43 @@ impl<'a> ReturnExpr<'a> { } } +// SelfKw +#[derive(Debug, Clone, Copy,)] +pub struct SelfKwNode = OwnedRoot> { + pub(crate) syntax: SyntaxNode, +} +pub type SelfKw<'a> = SelfKwNode>; + +impl, R2: TreeRoot> PartialEq> for SelfKwNode { + fn eq(&self, other: &SelfKwNode) -> bool { self.syntax == other.syntax } +} +impl> Eq for SelfKwNode {} +impl> Hash for SelfKwNode { + fn hash(&self, state: &mut H) { self.syntax.hash(state) } +} + +impl<'a> AstNode<'a> for SelfKw<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + SELF_KW => Some(SelfKw { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl> SelfKwNode { + pub fn borrowed(&self) -> SelfKw { + SelfKwNode { syntax: self.syntax.borrowed() } + } + pub fn owned(&self) -> SelfKwNode { + SelfKwNode { syntax: self.syntax.owned() } + } +} + + +impl<'a> SelfKw<'a> {} + // SelfParam #[derive(Debug, Clone, Copy,)] pub struct SelfParamNode = OwnedRoot> { @@ -3487,7 +3560,15 @@ impl> SelfParamNode { } -impl<'a> SelfParam<'a> {} +impl<'a> SelfParam<'a> { + pub fn type_ref(self) -> Option> { + super::child_opt(self) + } + + pub fn self_kw(self) -> Option> { + super::child_opt(self) + } +} // SlicePat #[derive(Debug, Clone, Copy,)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 9a4a96facb..c55e9e07a8 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -284,6 +284,7 @@ Grammar( options: [ "ItemList" ] ), "ItemList": ( + collections: [["impl_items", "ImplItem"]], traits: [ "FnDefOwner", "ModuleItemOwner" ], ), "ConstDef": ( traits: [ @@ -307,7 +308,7 @@ Grammar( "AttrsOwner", "DocCommentsOwner" ] ), - "ImplBlock": (collections: []), + "ImplBlock": (options: ["ItemList"]), "ParenType": (options: ["TypeRef"]), "TupleType": ( collections: [["fields", "TypeRef"]] ), @@ -351,6 +352,9 @@ Grammar( enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "TypeDef", "ImplBlock", "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ] ), + "ImplItem": ( + enum: ["FnDef", "TypeDef", "ConstDef"] + ), "TupleExpr": (), "ArrayExpr": (), @@ -530,7 +534,8 @@ Grammar( ["params", "Param"] ] ), - "SelfParam": (), + "SelfParam": (options: ["TypeRef", "SelfKw"]), + "SelfKw": (), "Param": ( options: [ "Pat", "TypeRef" ], ), diff --git a/crates/ra_syntax/src/grammar/items.rs b/crates/ra_syntax/src/grammar/items.rs index b9a00b5657..265e84570d 100644 --- a/crates/ra_syntax/src/grammar/items.rs +++ b/crates/ra_syntax/src/grammar/items.rs @@ -151,7 +151,7 @@ pub(super) fn maybe_item(p: &mut Parser, flavor: ItemFlavor) -> MaybeItem { // test unsafe_default_impl // unsafe default impl Foo {} IMPL_KW => { - traits::impl_item(p); + traits::impl_block(p); IMPL_BLOCK } _ => { diff --git a/crates/ra_syntax/src/grammar/items/traits.rs b/crates/ra_syntax/src/grammar/items/traits.rs index d4da8b2f72..0a0621753f 100644 --- a/crates/ra_syntax/src/grammar/items/traits.rs +++ b/crates/ra_syntax/src/grammar/items/traits.rs @@ -40,9 +40,9 @@ pub(crate) fn trait_item_list(p: &mut Parser) { m.complete(p, ITEM_LIST); } -// test impl_item +// test impl_block // impl Foo {} -pub(super) fn impl_item(p: &mut Parser) { +pub(super) fn impl_block(p: &mut Parser) { assert!(p.at(IMPL_KW)); p.bump(); if choose_type_params_over_qpath(p) { @@ -52,7 +52,7 @@ pub(super) fn impl_item(p: &mut Parser) { // TODO: never type // impl ! {} - // test impl_item_neg + // test impl_block_neg // impl !Send for X {} p.eat(EXCL); impl_type(p); diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.rs similarity index 100% rename from crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.rs rename to crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.rs diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.txt similarity index 100% rename from crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_item_neg.txt rename to crates/ra_syntax/tests/data/parser/inline/ok/0063_impl_block_neg.txt diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.rs similarity index 100% rename from crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.rs rename to crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.rs diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.txt similarity index 100% rename from crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_item.txt rename to crates/ra_syntax/tests/data/parser/inline/ok/0079_impl_block.txt