From ccfc6b11c1e55e28e42bb79414d8349e8eb36086 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 16 Feb 2019 20:29:36 +0100 Subject: Add a test for impl generics --- .../ty/snapshots/tests__infer_impl_generics.snap | 39 ++++++++++++++++++++++ crates/ra_hir/src/ty/tests.rs | 32 ++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap (limited to 'crates') diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap new file mode 100644 index 000000000..99df39fc2 --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap @@ -0,0 +1,39 @@ +--- +created: "2019-02-16T19:27:11.573533475Z" +creator: insta@0.6.2 +source: crates/ra_hir/src/ty/tests.rs +expression: "&result" +--- +[74; 78) 'self': A<[unknown], [unknown]> +[85; 107) '{ ... }': [unknown] +[95; 99) 'self': A<[unknown], [unknown]> +[95; 101) 'self.x': [unknown] +[117; 121) 'self': A<[unknown], [unknown]> +[128; 150) '{ ... }': [unknown] +[138; 142) 'self': A<[unknown], [unknown]> +[138; 144) 'self.y': [unknown] +[163; 167) 'self': A<[unknown], [unknown]> +[169; 170) 't': T +[188; 223) '{ ... }': ([unknown], [unknown], T) +[198; 217) '(self.....y, t)': ([unknown], [unknown], T) +[199; 203) 'self': A<[unknown], [unknown]> +[199; 205) 'self.x': [unknown] +[207; 211) 'self': A<[unknown], [unknown]> +[207; 213) 'self.y': [unknown] +[215; 216) 't': T +[245; 342) '{ ...(1); }': () +[255; 256) 'a': A +[259; 281) 'A { x:...1i64 }': A +[266; 270) '1u64': u64 +[275; 279) '1i64': i64 +[287; 288) 'a': A +[287; 292) 'a.x()': [unknown] +[298; 299) 'a': A +[298; 303) 'a.y()': [unknown] +[309; 310) 'a': A +[309; 319) 'a.z(1i128)': ([unknown], [unknown], i128) +[313; 318) '1i128': i128 +[325; 326) 'a': A +[325; 339) 'a.z::(1)': ([unknown], [unknown], i32) +[337; 338) '1': i32 + diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 203f1fe4d..1eca68dc5 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -506,6 +506,38 @@ fn test() { ); } +#[test] +fn infer_impl_generics() { + check_inference( + "infer_impl_generics", + r#" +struct A { + x: T1, + y: T2, +} +impl A { + fn x(self) -> X { + self.x + } + fn y(self) -> Y { + self.y + } + fn z(self, t: T) -> (X, Y, T) { + (self.x, self.y, t) + } +} + +fn test() -> i128 { + let a = A { x: 1u64, y: 1i64 }; + a.x(); + a.y(); + a.z(1i128); + a.z::(1); +} +"#, + ); +} + #[test] fn infer_generic_chain() { check_inference( -- cgit v1.2.3 From da7056245d9b59a4b3af7266dd271bab58cb6527 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 16 Feb 2019 21:19:24 +0100 Subject: Add generic params to impl blocks --- crates/ra_hir/src/generics.rs | 6 ++++-- crates/ra_hir/src/impl_block.rs | 16 ++++++++++------ crates/ra_syntax/src/ast/generated.rs | 1 + crates/ra_syntax/src/grammar.ron | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index c72360f44..6ae0ead1b 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use ra_syntax::ast::{self, NameOwner, TypeParamsOwner}; -use crate::{db::PersistentHirDatabase, Name, AsName, Function, Struct, Enum, Trait, Type}; +use crate::{db::PersistentHirDatabase, Name, AsName, Function, Struct, Enum, Trait, Type, ImplBlock}; /// Data about a generic parameter (to a function, struct, impl, ...). #[derive(Clone, PartialEq, Eq, Debug)] @@ -30,8 +30,9 @@ pub enum GenericDef { Enum(Enum), Trait(Trait), Type(Type), + ImplBlock(ImplBlock), } -impl_froms!(GenericDef: Function, Struct, Enum, Trait, Type); +impl_froms!(GenericDef: Function, Struct, Enum, Trait, Type, ImplBlock); impl GenericParams { pub(crate) fn generic_params_query( @@ -45,6 +46,7 @@ impl GenericParams { GenericDef::Enum(it) => generics.fill(&*it.source(db).1), GenericDef::Trait(it) => generics.fill(&*it.source(db).1), GenericDef::Type(it) => generics.fill(&*it.source(db).1), + GenericDef::ImplBlock(it) => generics.fill(&*it.source(db).1), } Arc::new(generics) diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 4d8bdf33a..7d862882d 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -13,7 +13,7 @@ use crate::{ type_ref::TypeRef, ids::LocationCtx, resolve::Resolver, - ty::Ty, + ty::Ty, generics::GenericParams }; use crate::code_model_api::{Module, ModuleSource}; @@ -38,7 +38,7 @@ impl ImplSourceMap { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ImplBlock { module: Module, impl_id: ImplId, @@ -58,7 +58,7 @@ impl ImplBlock { } /// Returns the syntax of the impl block - pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc) { + pub fn source(&self, db: &impl PersistentHirDatabase) -> (HirFileId, TreeArc) { let source_map = db.impls_in_module_source_map(self.module); let (file_id, source) = self.module.definition_source(db); (file_id, source_map.get(&source, self.impl_id)) @@ -72,11 +72,11 @@ impl ImplBlock { self.module } - pub fn target_trait_ref(&self, db: &impl HirDatabase) -> Option { + pub fn target_trait_ref(&self, db: &impl PersistentHirDatabase) -> Option { db.impls_in_module(self.module).impls[self.impl_id].target_trait().cloned() } - pub fn target_type(&self, db: &impl HirDatabase) -> TypeRef { + pub fn target_type(&self, db: &impl PersistentHirDatabase) -> TypeRef { db.impls_in_module(self.module).impls[self.impl_id].target_type().clone() } @@ -96,10 +96,14 @@ impl ImplBlock { None } - pub fn items(&self, db: &impl HirDatabase) -> Vec { + pub fn items(&self, db: &impl PersistentHirDatabase) -> Vec { db.impls_in_module(self.module).impls[self.impl_id].items().to_vec() } + pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc { + db.generic_params((*self).into()) + } + pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { let r = self.module().resolver(db); // TODO: add generics diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 7c5e8ce5e..aa9da92da 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -1352,6 +1352,7 @@ impl ToOwned for ImplBlock { } +impl ast::TypeParamsOwner for ImplBlock {} impl ImplBlock { pub fn item_list(&self) -> Option<&ItemList> { super::child_opt(self) diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 304bc5909..5ec68014b 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -322,7 +322,7 @@ Grammar( ], options: ["TypeRef"] ), - "ImplBlock": (options: ["ItemList"]), + "ImplBlock": (options: ["ItemList"], traits: ["TypeParamsOwner"]), "ParenType": (options: ["TypeRef"]), "TupleType": ( collections: [["fields", "TypeRef"]] ), -- cgit v1.2.3 From 2af067b391bf37950cf1d3f1af863d931b385af5 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 16 Feb 2019 21:21:36 +0100 Subject: Resolve impl generic params --- crates/ra_hir/src/impl_block.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 7d862882d..7ecf8c368 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -106,7 +106,9 @@ impl ImplBlock { pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { let r = self.module().resolver(db); - // TODO: add generics + // 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 }; let r = r.push_impl_block_scope(self.clone()); r } -- cgit v1.2.3 From 65bd9bc3a800e09f52a315cf98e86c120c366c2c Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 16 Feb 2019 22:06:23 +0100 Subject: Handle impl generics in method calls --- crates/ra_hir/src/code_model_api.rs | 36 ++++++++++++++- crates/ra_hir/src/code_model_impl/function.rs | 13 +----- crates/ra_hir/src/generics.rs | 38 +++++++++++---- crates/ra_hir/src/ids.rs | 3 +- crates/ra_hir/src/ty.rs | 15 ++++-- .../ty/snapshots/tests__infer_generic_chain.snap | 54 +++++++++++----------- .../ty/snapshots/tests__infer_impl_generics.snap | 40 ++++++++-------- 7 files changed, 122 insertions(+), 77 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 99f004905..26ebc445b 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -463,7 +463,7 @@ impl Function { self.id.source(db) } - pub fn module(&self, db: &impl HirDatabase) -> Module { + pub fn module(&self, db: &impl PersistentHirDatabase) -> Module { self.id.module(db) } @@ -497,6 +497,12 @@ impl Function { db.generic_params((*self).into()) } + /// The containing impl block, if this is a method. + pub fn impl_block(&self, db: &impl PersistentHirDatabase) -> Option { + let module_impls = db.impls_in_module(self.module(db)); + ImplBlock::containing(module_impls, (*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 { @@ -527,6 +533,16 @@ impl Const { pub fn source(&self, db: &impl PersistentHirDatabase) -> (HirFileId, TreeArc) { self.id.source(db) } + + pub fn module(&self, db: &impl PersistentHirDatabase) -> Module { + self.id.module(db) + } + + /// The containing impl block, if this is a method. + pub fn impl_block(&self, db: &impl PersistentHirDatabase) -> Option { + let module_impls = db.impls_in_module(self.module(db)); + ImplBlock::containing(module_impls, (*self).into()) + } } impl Docs for Const { @@ -544,6 +560,10 @@ impl Static { pub fn source(&self, db: &impl PersistentHirDatabase) -> (HirFileId, TreeArc) { self.id.source(db) } + + pub fn module(&self, db: &impl PersistentHirDatabase) -> Module { + self.id.module(db) + } } impl Docs for Static { @@ -562,6 +582,10 @@ impl Trait { self.id.source(db) } + pub fn module(&self, db: &impl PersistentHirDatabase) -> Module { + self.id.module(db) + } + pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc { db.generic_params((*self).into()) } @@ -586,6 +610,16 @@ impl Type { pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc { db.generic_params((*self).into()) } + + pub fn module(&self, db: &impl PersistentHirDatabase) -> Module { + self.id.module(db) + } + + /// The containing impl block, if this is a method. + pub fn impl_block(&self, db: &impl PersistentHirDatabase) -> Option { + let module_impls = db.impls_in_module(self.module(db)); + ImplBlock::containing(module_impls, (*self).into()) + } } impl Docs for Type { diff --git a/crates/ra_hir/src/code_model_impl/function.rs b/crates/ra_hir/src/code_model_impl/function.rs index b9438fdb7..c401528c6 100644 --- a/crates/ra_hir/src/code_model_impl/function.rs +++ b/crates/ra_hir/src/code_model_impl/function.rs @@ -3,22 +3,11 @@ use std::sync::Arc; use ra_syntax::ast::{self, NameOwner}; use crate::{ - HirDatabase, Name, AsName, Function, FnSignature, + Name, AsName, Function, FnSignature, type_ref::{TypeRef, Mutability}, PersistentHirDatabase, - impl_block::ImplBlock, }; -impl Function { - // 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 { - let module_impls = db.impls_in_module(self.module(db)); - ImplBlock::containing(module_impls, (*self).into()) - } -} - impl FnSignature { pub(crate) fn fn_signature_query( db: &impl PersistentHirDatabase, diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 6ae0ead1b..fcc513353 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs @@ -20,6 +20,7 @@ pub struct GenericParam { /// Data about the generic parameters of a function, struct, impl, etc. #[derive(Clone, PartialEq, Eq, Debug, Default)] pub struct GenericParams { + pub(crate) parent_params: Option>, pub(crate) params: Vec, } @@ -40,28 +41,36 @@ impl GenericParams { def: GenericDef, ) -> Arc { let mut generics = GenericParams::default(); + let parent = match def { + GenericDef::Function(it) => it.impl_block(db), + GenericDef::Type(it) => it.impl_block(db), + GenericDef::Struct(_) | GenericDef::Enum(_) | GenericDef::Trait(_) => None, + GenericDef::ImplBlock(_) => None, + }; + generics.parent_params = parent.map(|p| p.generic_params(db)); + let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32; match def { - GenericDef::Function(it) => generics.fill(&*it.source(db).1), - GenericDef::Struct(it) => generics.fill(&*it.source(db).1), - GenericDef::Enum(it) => generics.fill(&*it.source(db).1), - GenericDef::Trait(it) => generics.fill(&*it.source(db).1), - GenericDef::Type(it) => generics.fill(&*it.source(db).1), - GenericDef::ImplBlock(it) => generics.fill(&*it.source(db).1), + GenericDef::Function(it) => generics.fill(&*it.source(db).1, start), + GenericDef::Struct(it) => generics.fill(&*it.source(db).1, start), + GenericDef::Enum(it) => generics.fill(&*it.source(db).1, start), + GenericDef::Trait(it) => generics.fill(&*it.source(db).1, start), + GenericDef::Type(it) => generics.fill(&*it.source(db).1, start), + GenericDef::ImplBlock(it) => generics.fill(&*it.source(db).1, start), } Arc::new(generics) } - fn fill(&mut self, node: &impl TypeParamsOwner) { + fn fill(&mut self, node: &impl TypeParamsOwner, start: u32) { if let Some(params) = node.type_param_list() { - self.fill_params(params) + self.fill_params(params, start) } } - fn fill_params(&mut self, params: &ast::TypeParamList) { + fn fill_params(&mut self, params: &ast::TypeParamList, start: u32) { for (idx, type_param) in params.type_params().enumerate() { let name = type_param.name().map(AsName::as_name).unwrap_or_else(Name::missing); - let param = GenericParam { idx: idx as u32, name }; + let param = GenericParam { idx: idx as u32 + start, name }; self.params.push(param); } } @@ -69,4 +78,13 @@ impl GenericParams { pub(crate) fn find_by_name(&self, name: &Name) -> Option<&GenericParam> { self.params.iter().find(|p| &p.name == name) } + + pub fn count_parent_params(&self) -> usize { + self.parent_params.as_ref().map(|p| p.count_params_including_parent()).unwrap_or(0) + } + + pub fn count_params_including_parent(&self) -> usize { + let parent_count = self.count_parent_params(); + parent_count + self.params.len() + } } diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 3e11dd6ad..6df037859 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -9,7 +9,6 @@ use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, SyntaxNodePtr, ast}; use ra_arena::{Arena, RawId, ArenaId, impl_arena_id}; use crate::{ - HirDatabase, Module, PersistentHirDatabase, }; @@ -215,7 +214,7 @@ pub(crate) trait AstItemDef: ArenaId + Clone { N::cast(&syntax).unwrap_or_else(|| panic!("invalid ItemLoc: {:?}", loc.raw)).to_owned(); (loc.raw.file_id, ast) } - fn module(self, db: &impl HirDatabase) -> Module { + fn module(self, db: &impl PersistentHirDatabase) -> Module { let int = Self::interner(db.as_ref()); let loc = int.id2loc(self); loc.module diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index f28a7e731..db0a20514 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -421,7 +421,8 @@ impl Ty { (var.parent_enum(db).generic_params(db), segment) } }; - // substs_from_path + let parent_param_count = def_generics.count_parent_params(); + substs.extend((0..parent_param_count).map(|_| Ty::Unknown)); if let Some(generic_args) = &segment.args_and_bindings { // if args are provided, it should be all of them, but we can't rely on that let param_count = def_generics.params.len(); @@ -436,9 +437,8 @@ impl Ty { } // add placeholders for args that were not provided // TODO: handle defaults - let supplied_params = - segment.args_and_bindings.as_ref().map(|ga| ga.args.len()).unwrap_or(0); - for _ in supplied_params..def_generics.params.len() { + let supplied_params = substs.len(); + for _ in supplied_params..def_generics.count_params_including_parent() { substs.push(Ty::Unknown); } assert_eq!(substs.len(), def_generics.params.len()); @@ -666,7 +666,12 @@ fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty { } fn make_substs(generics: &GenericParams) -> Substs { - Substs(generics.params.iter().map(|_p| Ty::Unknown).collect::>().into()) + Substs( + (0..generics.count_params_including_parent()) + .map(|_p| Ty::Unknown) + .collect::>() + .into(), + ) } fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Ty { 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 626f31252..d1ce87b0a 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,36 +1,36 @@ --- -created: "2019-01-27T14:52:29.938713255+00:00" -creator: insta@0.5.2 -expression: "&result" +created: "2019-02-16T20:53:59.655361804Z" +creator: insta@0.6.2 source: crates/ra_hir/src/ty/tests.rs +expression: "&result" --- -[53; 57) 'self': A<[unknown]> -[65; 87) '{ ... }': [unknown] -[75; 79) 'self': A<[unknown]> -[75; 81) 'self.x': [unknown] +[53; 57) 'self': A +[65; 87) '{ ... }': T2 +[75; 79) 'self': A +[75; 81) 'self.x': T2 [99; 100) 't': T [110; 115) '{ t }': T [112; 113) 't': T [135; 261) '{ ....x() }': i128 -[146; 147) 'x': i32 -[150; 151) '1': i32 -[162; 163) 'y': i32 -[166; 168) 'id': fn id(T) -> T -[166; 171) 'id(x)': i32 -[169; 170) 'x': i32 -[182; 183) 'a': A -[186; 200) 'A { x: id(y) }': A -[193; 195) 'id': fn id(T) -> T -[193; 198) 'id(y)': i32 -[196; 197) 'y': i32 -[211; 212) 'z': i32 -[215; 217) 'id': fn id(T) -> T -[215; 222) 'id(a.x)': i32 -[218; 219) 'a': A -[218; 221) 'a.x': i32 -[233; 234) 'b': A -[237; 247) 'A { x: z }': A -[244; 245) 'z': i32 -[254; 255) 'b': A +[146; 147) 'x': i128 +[150; 151) '1': i128 +[162; 163) 'y': i128 +[166; 168) 'id': fn id(T) -> T +[166; 171) 'id(x)': i128 +[169; 170) 'x': i128 +[182; 183) 'a': A +[186; 200) 'A { x: id(y) }': A +[193; 195) 'id': fn id(T) -> T +[193; 198) 'id(y)': i128 +[196; 197) 'y': i128 +[211; 212) 'z': i128 +[215; 217) 'id': fn id(T) -> T +[215; 222) 'id(a.x)': i128 +[218; 219) 'a': A +[218; 221) 'a.x': i128 +[233; 234) 'b': A +[237; 247) 'A { x: z }': A +[244; 245) 'z': i128 +[254; 255) 'b': A [254; 259) 'b.x()': i128 diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap index 99df39fc2..eabbe52fd 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap @@ -1,25 +1,25 @@ --- -created: "2019-02-16T19:27:11.573533475Z" +created: "2019-02-16T20:53:59.657979128Z" creator: insta@0.6.2 source: crates/ra_hir/src/ty/tests.rs expression: "&result" --- -[74; 78) 'self': A<[unknown], [unknown]> -[85; 107) '{ ... }': [unknown] -[95; 99) 'self': A<[unknown], [unknown]> -[95; 101) 'self.x': [unknown] -[117; 121) 'self': A<[unknown], [unknown]> -[128; 150) '{ ... }': [unknown] -[138; 142) 'self': A<[unknown], [unknown]> -[138; 144) 'self.y': [unknown] -[163; 167) 'self': A<[unknown], [unknown]> +[74; 78) 'self': A +[85; 107) '{ ... }': X +[95; 99) 'self': A +[95; 101) 'self.x': X +[117; 121) 'self': A +[128; 150) '{ ... }': Y +[138; 142) 'self': A +[138; 144) 'self.y': Y +[163; 167) 'self': A [169; 170) 't': T -[188; 223) '{ ... }': ([unknown], [unknown], T) -[198; 217) '(self.....y, t)': ([unknown], [unknown], T) -[199; 203) 'self': A<[unknown], [unknown]> -[199; 205) 'self.x': [unknown] -[207; 211) 'self': A<[unknown], [unknown]> -[207; 213) 'self.y': [unknown] +[188; 223) '{ ... }': (X, Y, T) +[198; 217) '(self.....y, t)': (X, Y, T) +[199; 203) 'self': A +[199; 205) 'self.x': X +[207; 211) 'self': A +[207; 213) 'self.y': Y [215; 216) 't': T [245; 342) '{ ...(1); }': () [255; 256) 'a': A @@ -27,13 +27,13 @@ expression: "&result" [266; 270) '1u64': u64 [275; 279) '1i64': i64 [287; 288) 'a': A -[287; 292) 'a.x()': [unknown] +[287; 292) 'a.x()': u64 [298; 299) 'a': A -[298; 303) 'a.y()': [unknown] +[298; 303) 'a.y()': i64 [309; 310) 'a': A -[309; 319) 'a.z(1i128)': ([unknown], [unknown], i128) +[309; 319) 'a.z(1i128)': (u64, i64, i128) [313; 318) '1i128': i128 [325; 326) 'a': A -[325; 339) 'a.z::(1)': ([unknown], [unknown], i32) +[325; 339) 'a.z::(1)': (u64, i64, i32) [337; 338) '1': i32 -- cgit v1.2.3 From a1bda3fc084bb6aa4979282b4907db9885fac9af Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 16 Feb 2019 23:05:57 +0100 Subject: Handle generic args for method calls --- crates/ra_hir/src/expr.rs | 9 +++-- crates/ra_hir/src/path.rs | 2 +- crates/ra_hir/src/ty.rs | 38 +++++++++++++++++++--- .../ty/snapshots/tests__infer_impl_generics.snap | 6 ++-- crates/ra_syntax/src/ast/generated.rs | 4 +++ crates/ra_syntax/src/grammar.ron | 2 +- 6 files changed, 50 insertions(+), 11 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 4e73590d0..e9db8282f 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -14,7 +14,7 @@ use crate::{ name::AsName, type_ref::{Mutability, TypeRef}, }; -use crate::ty::primitive::{UintTy, UncertainIntTy, UncertainFloatTy}; +use crate::{ path::GenericArgs, ty::primitive::{UintTy, UncertainIntTy, UncertainFloatTy}}; pub use self::scope::{ExprScopes, ScopesWithSyntaxMapping, ScopeEntryWithSyntax}; @@ -193,6 +193,7 @@ pub enum Expr { receiver: ExprId, method_name: Name, args: Vec, + generic_args: Option, }, Match { expr: ExprId, @@ -597,7 +598,11 @@ impl ExprCollector { Vec::new() }; let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); - self.alloc_expr(Expr::MethodCall { receiver, method_name, args }, syntax_ptr) + let generic_args = e.type_arg_list().and_then(GenericArgs::from_ast); + self.alloc_expr( + Expr::MethodCall { receiver, method_name, args, generic_args }, + syntax_ptr, + ) } ast::ExprKind::MatchExpr(e) => { let expr = self.collect_expr_opt(e.expr()); diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 8ed54607a..83bbbb741 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -129,7 +129,7 @@ impl Path { } impl GenericArgs { - fn from_ast(node: &ast::TypeArgList) -> Option { + pub fn from_ast(node: &ast::TypeArgList) -> Option { let mut args = Vec::new(); for type_arg in node.type_args() { let type_ref = TypeRef::from_ast_opt(type_arg.type_ref()); diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index db0a20514..f32c77faf 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -165,6 +165,17 @@ impl Substs { pub fn empty() -> Substs { Substs(Arc::new([])) } + + /// Replaces the end of the substitutions by other ones. + pub(crate) fn replace_tail(self, replace_by: Vec) -> Substs { + // again missing Arc::make_mut_slice... + let len = replace_by.len().min(self.0.len()); + let parent_len = self.0.len() - len; + let mut result = Vec::with_capacity(parent_len + len); + result.extend(self.0.iter().take(parent_len).cloned()); + result.extend(replace_by); + Substs(result.into()) + } } /// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs). @@ -1367,15 +1378,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } ret_ty } - Expr::MethodCall { receiver, args, method_name } => { + Expr::MethodCall { receiver, args, method_name, generic_args } => { let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); let resolved = receiver_ty.clone().lookup_method(self.db, method_name); - let method_ty = match resolved { + let (method_ty, def_generics) = match resolved { Some(func) => { self.write_method_resolution(tgt_expr, func); - self.db.type_for_def(func.into()) + (self.db.type_for_def(func.into()), Some(func.generic_params(self.db))) + } + None => (Ty::Unknown, None), + }; + // handle provided type arguments + let method_ty = if let Some(generic_args) = generic_args { + // if args are provided, it should be all of them, but we can't rely on that + let param_count = def_generics.map(|g| g.params.len()).unwrap_or(0); + let mut new_substs = Vec::with_capacity(generic_args.args.len()); + for arg in generic_args.args.iter().take(param_count) { + match arg { + GenericArg::Type(type_ref) => { + let ty = self.make_ty(type_ref); + new_substs.push(ty); + } + } } - None => Ty::Unknown, + let substs = method_ty.substs().unwrap_or_else(Substs::empty); + let substs = substs.replace_tail(new_substs); + method_ty.apply_substs(substs) + } else { + method_ty }; let method_ty = self.insert_type_vars(method_ty); let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty { diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap index eabbe52fd..44f00a52e 100644 --- a/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics.snap @@ -1,5 +1,5 @@ --- -created: "2019-02-16T20:53:59.657979128Z" +created: "2019-02-16T21:58:14.029368845Z" creator: insta@0.6.2 source: crates/ra_hir/src/ty/tests.rs expression: "&result" @@ -34,6 +34,6 @@ expression: "&result" [309; 319) 'a.z(1i128)': (u64, i64, i128) [313; 318) '1i128': i128 [325; 326) 'a': A -[325; 339) 'a.z::(1)': (u64, i64, i32) -[337; 338) '1': i32 +[325; 339) 'a.z::(1)': (u64, i64, u128) +[337; 338) '1': u128 diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index aa9da92da..dd91b5063 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -2095,6 +2095,10 @@ impl MethodCallExpr { pub fn name_ref(&self) -> Option<&NameRef> { super::child_opt(self) } + + pub fn type_arg_list(&self) -> Option<&TypeArgList> { + super::child_opt(self) + } } // Module diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 5ec68014b..27a123681 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -431,7 +431,7 @@ Grammar( ), "MethodCallExpr": ( traits: ["ArgListOwner"], - options: [ "Expr", "NameRef" ], + options: [ "Expr", "NameRef", "TypeArgList" ], ), "IndexExpr": (), "FieldExpr": (options: ["Expr", "NameRef"]), -- cgit v1.2.3 From 795d718ba17545aedb0475051332aed6db2104ed Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 17 Feb 2019 14:43:59 +0100 Subject: Unify with the autorefed/autoderefed receiver type during method resolution --- crates/ra_hir/src/ty.rs | 18 +++++++++++------- crates/ra_hir/src/ty/method_resolution.rs | 12 +++++++----- .../tests__infer_impl_generics_with_autoderef.snap | 16 ++++++++++++++++ crates/ra_hir/src/ty/tests.rs | 20 ++++++++++++++++++++ crates/ra_ide_api/src/completion/complete_dot.rs | 2 +- 5 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap (limited to 'crates') diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index f32c77faf..562ad1f49 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -1381,12 +1381,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Expr::MethodCall { receiver, args, method_name, generic_args } => { let receiver_ty = self.infer_expr(*receiver, &Expectation::none()); let resolved = receiver_ty.clone().lookup_method(self.db, method_name); - let (method_ty, def_generics) = match resolved { - Some(func) => { + let (derefed_receiver_ty, method_ty, def_generics) = match resolved { + Some((ty, func)) => { self.write_method_resolution(tgt_expr, func); - (self.db.type_for_def(func.into()), Some(func.generic_params(self.db))) + (ty, self.db.type_for_def(func.into()), Some(func.generic_params(self.db))) } - None => (Ty::Unknown, None), + None => (Ty::Unknown, receiver_ty, None), }; // handle provided type arguments let method_ty = if let Some(generic_args) = generic_args { @@ -1429,9 +1429,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } _ => (Ty::Unknown, Vec::new(), Ty::Unknown), }; - // TODO we would have to apply the autoderef/autoref steps here - // to get the correct receiver type to unify... - self.unify(&expected_receiver_ty, &receiver_ty); + // Apply autoref so the below unification works correctly + let actual_receiver_ty = match expected_receiver_ty { + Ty::Ref(_, mutability) => Ty::Ref(Arc::new(derefed_receiver_ty), mutability), + _ => derefed_receiver_ty, + }; + self.unify(&expected_receiver_ty, &actual_receiver_ty); + let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown)); for (arg, param) in args.iter().zip(param_iter) { self.infer_expr(*arg, &Expectation::has_type(param)); diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 8d1076774..94b757af2 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -118,11 +118,13 @@ impl Ty { // TODO: cache this as a query? // - if so, what signature? (TyFingerprint, Name)? // - or maybe cache all names and def_ids of methods per fingerprint? - pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option { - self.iterate_methods(db, |f| { + /// Look up the method with the given name, returning the actual autoderefed + /// receiver type (but without autoref applied yet). + pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<(Ty, Function)> { + self.iterate_methods(db, |ty, f| { let sig = f.signature(db); if sig.name() == name && sig.has_self_param() { - Some(f) + Some((ty.clone(), f)) } else { None } @@ -134,7 +136,7 @@ impl Ty { pub fn iterate_methods( self, db: &impl HirDatabase, - mut callback: impl FnMut(Function) -> Option, + mut callback: impl FnMut(&Ty, Function) -> Option, ) -> Option { // For method calls, rust first does any number of autoderef, and then one // autoref (i.e. when the method takes &self or &mut self). We just ignore @@ -156,7 +158,7 @@ impl Ty { for item in impl_block.items(db) { match item { ImplItem::Method(f) => { - if let Some(result) = callback(f) { + if let Some(result) = callback(&derefed_ty, f) { return Some(result); } } diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap new file mode 100644 index 000000000..f609eaf7c --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_impl_generics_with_autoderef.snap @@ -0,0 +1,16 @@ +--- +created: "2019-02-17T13:35:06.385679926Z" +creator: insta@0.6.2 +source: crates/ra_hir/src/ty/tests.rs +expression: "&result" +--- +[78; 82) 'self': &Option +[98; 100) '{}': () +[111; 112) 'o': Option +[127; 165) '{ ...f(); }': () +[133; 146) '(&o).as_ref()': Option<&u32> +[134; 136) '&o': &Option +[135; 136) 'o': Option +[152; 153) 'o': Option +[152; 162) 'o.as_ref()': Option<&u32> + diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 1eca68dc5..5eb9c4f5b 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -538,6 +538,26 @@ fn test() -> i128 { ); } +#[test] +fn infer_impl_generics_with_autoderef() { + check_inference( + "infer_impl_generics_with_autoderef", + r#" +enum Option { + Some(T), + None, +} +impl Option { + fn as_ref(&self) -> Option<&T> {} +} +fn test(o: Option) { + (&o).as_ref(); + o.as_ref(); +} +"#, + ); +} + #[test] fn infer_generic_chain() { check_inference( diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index be839345f..20fa323ce 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs @@ -63,7 +63,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) } fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { - receiver.iterate_methods(ctx.db, |func| { + receiver.iterate_methods(ctx.db, |_ty, func| { let sig = func.signature(ctx.db); if sig.has_self_param() { CompletionItem::new( -- cgit v1.2.3 From 3c7c7e5a04306f8b68dffef2b5ca84628ed81ce2 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 17 Feb 2019 14:54:48 +0100 Subject: Make GenericArgs::from_ast pub(crate) --- crates/ra_hir/src/path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 83bbbb741..6ca373e34 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -129,7 +129,7 @@ impl Path { } impl GenericArgs { - pub fn from_ast(node: &ast::TypeArgList) -> Option { + pub(crate) fn from_ast(node: &ast::TypeArgList) -> Option { let mut args = Vec::new(); for type_arg in node.type_args() { let type_ref = TypeRef::from_ast_opt(type_arg.type_ref()); -- cgit v1.2.3