diff options
author | Florian Diebold <[email protected]> | 2021-05-23 15:59:23 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2021-05-23 17:24:21 +0100 |
commit | 4a6cdd776d403bacce0a5471d77e8c76695c5bc5 (patch) | |
tree | 63f29d9de92697b522c6cc274324764c31ef904f | |
parent | 96e5412f881608d703df129ed87f3488ad39a9e1 (diff) |
Record method call substs and use them in call info
-rw-r--r-- | crates/hir/src/semantics.rs | 10 | ||||
-rw-r--r-- | crates/hir/src/source_analyzer.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/expr.rs | 12 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/unsafe_check.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/infer.rs | 25 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/expr.rs | 34 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/unify.rs | 7 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 60 | ||||
-rw-r--r-- | crates/ide_db/src/call_info/tests.rs | 18 |
9 files changed, 125 insertions, 45 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 1b5064b5a..d65dd7df0 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -11,7 +11,7 @@ use hir_def::{ | |||
11 | AsMacroCall, FunctionId, TraitId, VariantId, | 11 | AsMacroCall, FunctionId, TraitId, VariantId, |
12 | }; | 12 | }; |
13 | use hir_expand::{name::AsName, ExpansionInfo}; | 13 | use hir_expand::{name::AsName, ExpansionInfo}; |
14 | use hir_ty::associated_type_shorthand_candidates; | 14 | use hir_ty::{associated_type_shorthand_candidates, Interner}; |
15 | use itertools::Itertools; | 15 | use itertools::Itertools; |
16 | use rustc_hash::{FxHashMap, FxHashSet}; | 16 | use rustc_hash::{FxHashMap, FxHashSet}; |
17 | use syntax::{ | 17 | use syntax::{ |
@@ -501,14 +501,12 @@ impl<'db> SemanticsImpl<'db> { | |||
501 | } | 501 | } |
502 | 502 | ||
503 | fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> { | 503 | fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> { |
504 | self.analyze(call.syntax()).resolve_method_call(self.db, call) | 504 | self.analyze(call.syntax()).resolve_method_call(self.db, call).map(|(id, _)| id) |
505 | } | 505 | } |
506 | 506 | ||
507 | fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { | 507 | fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { |
508 | // FIXME: this erases Substs, we should instead record the correct | 508 | let (func, subst) = self.analyze(call.syntax()).resolve_method_call(self.db, call)?; |
509 | // substitution during inference and use that | 509 | let ty = self.db.value_ty(func.into()).substitute(&Interner, &subst); |
510 | let func = self.resolve_method_call(call)?; | ||
511 | let ty = hir_ty::TyBuilder::value_ty(self.db, func.into()).fill_with_unknown().build(); | ||
512 | let resolver = self.analyze(call.syntax()).resolver; | 510 | let resolver = self.analyze(call.syntax()).resolver; |
513 | let ty = Type::new_with_resolver(self.db, &resolver, ty)?; | 511 | let ty = Type::new_with_resolver(self.db, &resolver, ty)?; |
514 | let mut res = ty.as_callable(self.db)?; | 512 | let mut res = ty.as_callable(self.db)?; |
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index b5c65808e..a1a9c727a 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs | |||
@@ -143,7 +143,7 @@ impl SourceAnalyzer { | |||
143 | &self, | 143 | &self, |
144 | db: &dyn HirDatabase, | 144 | db: &dyn HirDatabase, |
145 | call: &ast::MethodCallExpr, | 145 | call: &ast::MethodCallExpr, |
146 | ) -> Option<FunctionId> { | 146 | ) -> Option<(FunctionId, Substitution)> { |
147 | let expr_id = self.expr_id(db, &call.clone().into())?; | 147 | let expr_id = self.expr_id(db, &call.clone().into())?; |
148 | self.infer.as_ref()?.method_resolution(expr_id) | 148 | self.infer.as_ref()?.method_resolution(expr_id) |
149 | } | 149 | } |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 53c4ee9da..d1f113e7f 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -181,7 +181,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
181 | for (id, expr) in body.exprs.iter() { | 181 | for (id, expr) in body.exprs.iter() { |
182 | if let Expr::MethodCall { receiver, .. } = expr { | 182 | if let Expr::MethodCall { receiver, .. } = expr { |
183 | let function_id = match self.infer.method_resolution(id) { | 183 | let function_id = match self.infer.method_resolution(id) { |
184 | Some(id) => id, | 184 | Some((id, _)) => id, |
185 | None => continue, | 185 | None => continue, |
186 | }; | 186 | }; |
187 | 187 | ||
@@ -239,15 +239,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
239 | return; | 239 | return; |
240 | } | 240 | } |
241 | 241 | ||
242 | // FIXME: note that we erase information about substs here. This | 242 | let (callee, subst) = match self.infer.method_resolution(call_id) { |
243 | // is not right, but, luckily, doesn't matter as we care only | 243 | Some(it) => it, |
244 | // about the number of params | ||
245 | let callee = match self.infer.method_resolution(call_id) { | ||
246 | Some(callee) => callee, | ||
247 | None => return, | 244 | None => return, |
248 | }; | 245 | }; |
249 | let sig = | 246 | let sig = db.callable_item_signature(callee.into()).substitute(&Interner, &subst); |
250 | db.callable_item_signature(callee.into()).into_value_and_skipped_binders().0; | ||
251 | 247 | ||
252 | (sig, args) | 248 | (sig, args) |
253 | } | 249 | } |
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs index ed97dc0e3..5d13bddea 100644 --- a/crates/hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -105,7 +105,7 @@ fn walk_unsafe( | |||
105 | Expr::MethodCall { .. } => { | 105 | Expr::MethodCall { .. } => { |
106 | if infer | 106 | if infer |
107 | .method_resolution(current) | 107 | .method_resolution(current) |
108 | .map(|func| db.function_data(func).is_unsafe()) | 108 | .map(|(func, _)| db.function_data(func).is_unsafe()) |
109 | .unwrap_or(false) | 109 | .unwrap_or(false) |
110 | { | 110 | { |
111 | unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); | 111 | unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); |
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index f1cebbdb9..db3c937ff 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -37,8 +37,8 @@ use syntax::SmolStr; | |||
37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; | 37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; |
38 | use crate::{ | 38 | use crate::{ |
39 | db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, | 39 | db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, |
40 | lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, TyBuilder, | 40 | lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, |
41 | TyExt, TyKind, | 41 | TyBuilder, TyExt, TyKind, |
42 | }; | 42 | }; |
43 | 43 | ||
44 | // This lint has a false positive here. See the link below for details. | 44 | // This lint has a false positive here. See the link below for details. |
@@ -132,7 +132,7 @@ impl Default for InternedStandardTypes { | |||
132 | #[derive(Clone, PartialEq, Eq, Debug, Default)] | 132 | #[derive(Clone, PartialEq, Eq, Debug, Default)] |
133 | pub struct InferenceResult { | 133 | pub struct InferenceResult { |
134 | /// For each method call expr, records the function it resolves to. | 134 | /// For each method call expr, records the function it resolves to. |
135 | method_resolutions: FxHashMap<ExprId, FunctionId>, | 135 | method_resolutions: FxHashMap<ExprId, (FunctionId, Substitution)>, |
136 | /// For each field access expr, records the field it resolves to. | 136 | /// For each field access expr, records the field it resolves to. |
137 | field_resolutions: FxHashMap<ExprId, FieldId>, | 137 | field_resolutions: FxHashMap<ExprId, FieldId>, |
138 | /// For each struct literal or pattern, records the variant it resolves to. | 138 | /// For each struct literal or pattern, records the variant it resolves to. |
@@ -152,8 +152,8 @@ pub struct InferenceResult { | |||
152 | } | 152 | } |
153 | 153 | ||
154 | impl InferenceResult { | 154 | impl InferenceResult { |
155 | pub fn method_resolution(&self, expr: ExprId) -> Option<FunctionId> { | 155 | pub fn method_resolution(&self, expr: ExprId) -> Option<(FunctionId, Substitution)> { |
156 | self.method_resolutions.get(&expr).copied() | 156 | self.method_resolutions.get(&expr).cloned() |
157 | } | 157 | } |
158 | pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> { | 158 | pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> { |
159 | self.field_resolutions.get(&expr).copied() | 159 | self.field_resolutions.get(&expr).copied() |
@@ -284,14 +284,17 @@ impl<'a> InferenceContext<'a> { | |||
284 | self.table.propagate_diverging_flag(); | 284 | self.table.propagate_diverging_flag(); |
285 | let mut result = std::mem::take(&mut self.result); | 285 | let mut result = std::mem::take(&mut self.result); |
286 | for ty in result.type_of_expr.values_mut() { | 286 | for ty in result.type_of_expr.values_mut() { |
287 | *ty = self.table.resolve_ty_completely(ty.clone()); | 287 | *ty = self.table.resolve_completely(ty.clone()); |
288 | } | 288 | } |
289 | for ty in result.type_of_pat.values_mut() { | 289 | for ty in result.type_of_pat.values_mut() { |
290 | *ty = self.table.resolve_ty_completely(ty.clone()); | 290 | *ty = self.table.resolve_completely(ty.clone()); |
291 | } | 291 | } |
292 | for mismatch in result.type_mismatches.values_mut() { | 292 | for mismatch in result.type_mismatches.values_mut() { |
293 | mismatch.expected = self.table.resolve_ty_completely(mismatch.expected.clone()); | 293 | mismatch.expected = self.table.resolve_completely(mismatch.expected.clone()); |
294 | mismatch.actual = self.table.resolve_ty_completely(mismatch.actual.clone()); | 294 | mismatch.actual = self.table.resolve_completely(mismatch.actual.clone()); |
295 | } | ||
296 | for (_, subst) in result.method_resolutions.values_mut() { | ||
297 | *subst = self.table.resolve_completely(subst.clone()); | ||
295 | } | 298 | } |
296 | result | 299 | result |
297 | } | 300 | } |
@@ -300,8 +303,8 @@ impl<'a> InferenceContext<'a> { | |||
300 | self.result.type_of_expr.insert(expr, ty); | 303 | self.result.type_of_expr.insert(expr, ty); |
301 | } | 304 | } |
302 | 305 | ||
303 | fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId) { | 306 | fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) { |
304 | self.result.method_resolutions.insert(expr, func); | 307 | self.result.method_resolutions.insert(expr, (func, subst)); |
305 | } | 308 | } |
306 | 309 | ||
307 | fn write_field_resolution(&mut self, expr: ExprId, field: FieldId) { | 310 | fn write_field_resolution(&mut self, expr: ExprId, field: FieldId) { |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 08c05c67c..eab8fac91 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -891,17 +891,21 @@ impl<'a> InferenceContext<'a> { | |||
891 | method_name, | 891 | method_name, |
892 | ) | 892 | ) |
893 | }); | 893 | }); |
894 | let (derefed_receiver_ty, method_ty, def_generics) = match resolved { | 894 | let (derefed_receiver_ty, method_ty, substs) = match resolved { |
895 | Some((ty, func)) => { | 895 | Some((ty, func)) => { |
896 | let ty = canonicalized_receiver.decanonicalize_ty(ty); | 896 | let ty = canonicalized_receiver.decanonicalize_ty(ty); |
897 | self.write_method_resolution(tgt_expr, func); | 897 | let generics = generics(self.db.upcast(), func.into()); |
898 | (ty, self.db.value_ty(func.into()), Some(generics(self.db.upcast(), func.into()))) | 898 | let substs = self.substs_for_method_call(generics, generic_args, &ty); |
899 | self.write_method_resolution(tgt_expr, func, substs.clone()); | ||
900 | (ty, self.db.value_ty(func.into()), substs) | ||
899 | } | 901 | } |
900 | None => (receiver_ty, Binders::empty(&Interner, self.err_ty()), None), | 902 | None => ( |
903 | receiver_ty, | ||
904 | Binders::empty(&Interner, self.err_ty()), | ||
905 | Substitution::empty(&Interner), | ||
906 | ), | ||
901 | }; | 907 | }; |
902 | let substs = self.substs_for_method_call(def_generics, generic_args, &derefed_receiver_ty); | ||
903 | let method_ty = method_ty.substitute(&Interner, &substs); | 908 | let method_ty = method_ty.substitute(&Interner, &substs); |
904 | let method_ty = self.insert_type_vars(method_ty); | ||
905 | self.register_obligations_for_call(&method_ty); | 909 | self.register_obligations_for_call(&method_ty); |
906 | let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) { | 910 | let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) { |
907 | Some(sig) => { | 911 | Some(sig) => { |
@@ -950,23 +954,21 @@ impl<'a> InferenceContext<'a> { | |||
950 | 954 | ||
951 | fn substs_for_method_call( | 955 | fn substs_for_method_call( |
952 | &mut self, | 956 | &mut self, |
953 | def_generics: Option<Generics>, | 957 | def_generics: Generics, |
954 | generic_args: Option<&GenericArgs>, | 958 | generic_args: Option<&GenericArgs>, |
955 | receiver_ty: &Ty, | 959 | receiver_ty: &Ty, |
956 | ) -> Substitution { | 960 | ) -> Substitution { |
957 | let (parent_params, self_params, type_params, impl_trait_params) = | 961 | let (parent_params, self_params, type_params, impl_trait_params) = |
958 | def_generics.as_ref().map_or((0, 0, 0, 0), |g| g.provenance_split()); | 962 | def_generics.provenance_split(); |
959 | assert_eq!(self_params, 0); // method shouldn't have another Self param | 963 | assert_eq!(self_params, 0); // method shouldn't have another Self param |
960 | let total_len = parent_params + type_params + impl_trait_params; | 964 | let total_len = parent_params + type_params + impl_trait_params; |
961 | let mut substs = Vec::with_capacity(total_len); | 965 | let mut substs = Vec::with_capacity(total_len); |
962 | // Parent arguments are unknown, except for the receiver type | 966 | // Parent arguments are unknown, except for the receiver type |
963 | if let Some(parent_generics) = def_generics.as_ref().map(|p| p.iter_parent()) { | 967 | for (_id, param) in def_generics.iter_parent() { |
964 | for (_id, param) in parent_generics { | 968 | if param.provenance == hir_def::generics::TypeParamProvenance::TraitSelf { |
965 | if param.provenance == hir_def::generics::TypeParamProvenance::TraitSelf { | 969 | substs.push(receiver_ty.clone()); |
966 | substs.push(receiver_ty.clone()); | 970 | } else { |
967 | } else { | 971 | substs.push(self.table.new_type_var()); |
968 | substs.push(self.err_ty()); | ||
969 | } | ||
970 | } | 972 | } |
971 | } | 973 | } |
972 | // handle provided type arguments | 974 | // handle provided type arguments |
@@ -989,7 +991,7 @@ impl<'a> InferenceContext<'a> { | |||
989 | }; | 991 | }; |
990 | let supplied_params = substs.len(); | 992 | let supplied_params = substs.len(); |
991 | for _ in supplied_params..total_len { | 993 | for _ in supplied_params..total_len { |
992 | substs.push(self.err_ty()); | 994 | substs.push(self.table.new_type_var()); |
993 | } | 995 | } |
994 | assert_eq!(substs.len(), total_len); | 996 | assert_eq!(substs.len(), total_len); |
995 | Substitution::from_iter(&Interner, substs) | 997 | Substitution::from_iter(&Interner, substs) |
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index f8233cac3..ea5684229 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs | |||
@@ -295,8 +295,11 @@ impl<'a> InferenceTable<'a> { | |||
295 | .expect("fold failed unexpectedly") | 295 | .expect("fold failed unexpectedly") |
296 | } | 296 | } |
297 | 297 | ||
298 | pub(crate) fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { | 298 | pub(crate) fn resolve_completely<T>(&mut self, t: T) -> T::Result |
299 | self.resolve_with_fallback(ty, |_, _, d, _| d) | 299 | where |
300 | T: HasInterner<Interner = Interner> + Fold<Interner>, | ||
301 | { | ||
302 | self.resolve_with_fallback(t, |_, _, d, _| d) | ||
300 | } | 303 | } |
301 | 304 | ||
302 | /// Unify two types and register new trait goals that arise from that. | 305 | /// Unify two types and register new trait goals that arise from that. |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 787eb2fd3..c929d7394 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -785,6 +785,19 @@ fn foo() { | |||
785 | } | 785 | } |
786 | 786 | ||
787 | #[test] | 787 | #[test] |
788 | fn expected_type_generic_struct_field() { | ||
789 | check_expected_type_and_name( | ||
790 | r#" | ||
791 | struct Foo<T> { a: T } | ||
792 | fn foo() -> Foo<u32> { | ||
793 | Foo { a: $0 } | ||
794 | } | ||
795 | "#, | ||
796 | expect![[r#"ty: u32, name: a"#]], | ||
797 | ) | ||
798 | } | ||
799 | |||
800 | #[test] | ||
788 | fn expected_type_struct_field_with_leading_char() { | 801 | fn expected_type_struct_field_with_leading_char() { |
789 | cov_mark::check!(expected_type_struct_field_with_leading_char); | 802 | cov_mark::check!(expected_type_struct_field_with_leading_char); |
790 | check_expected_type_and_name( | 803 | check_expected_type_and_name( |
@@ -895,4 +908,51 @@ fn foo() -> u32 { | |||
895 | expect![[r#"ty: u32, name: ?"#]], | 908 | expect![[r#"ty: u32, name: ?"#]], |
896 | ) | 909 | ) |
897 | } | 910 | } |
911 | |||
912 | #[test] | ||
913 | fn expected_type_closure_param() { | ||
914 | check_expected_type_and_name( | ||
915 | r#" | ||
916 | fn foo() { | ||
917 | bar(|| $0); | ||
918 | } | ||
919 | |||
920 | fn bar(f: impl FnOnce() -> u32) {} | ||
921 | #[lang = "fn_once"] | ||
922 | trait FnOnce { type Output; } | ||
923 | "#, | ||
924 | expect![[r#"ty: u32, name: ?"#]], | ||
925 | ); | ||
926 | } | ||
927 | |||
928 | #[test] | ||
929 | fn expected_type_generic_function() { | ||
930 | check_expected_type_and_name( | ||
931 | r#" | ||
932 | fn foo() { | ||
933 | bar::<u32>($0); | ||
934 | } | ||
935 | |||
936 | fn bar<T>(t: T) {} | ||
937 | "#, | ||
938 | expect![[r#"ty: u32, name: t"#]], | ||
939 | ); | ||
940 | } | ||
941 | |||
942 | #[test] | ||
943 | fn expected_type_generic_method() { | ||
944 | check_expected_type_and_name( | ||
945 | r#" | ||
946 | fn foo() { | ||
947 | S(1u32).bar($0); | ||
948 | } | ||
949 | |||
950 | struct S<T>(T); | ||
951 | impl<T> S<T> { | ||
952 | fn bar(self, t: T) {} | ||
953 | } | ||
954 | "#, | ||
955 | expect![[r#"ty: u32, name: t"#]], | ||
956 | ); | ||
957 | } | ||
898 | } | 958 | } |
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs index be1cc12de..1aeda08e5 100644 --- a/crates/ide_db/src/call_info/tests.rs +++ b/crates/ide_db/src/call_info/tests.rs | |||
@@ -189,6 +189,24 @@ fn main() { S.foo($0); } | |||
189 | } | 189 | } |
190 | 190 | ||
191 | #[test] | 191 | #[test] |
192 | fn test_fn_signature_for_generic_method() { | ||
193 | check( | ||
194 | r#" | ||
195 | struct S<T>(T); | ||
196 | impl<T> S<T> { | ||
197 | fn foo(&self, x: T) {} | ||
198 | } | ||
199 | |||
200 | fn main() { S(1u32).foo($0); } | ||
201 | "#, | ||
202 | expect![[r#" | ||
203 | fn foo(&self, x: u32) | ||
204 | (<x: u32>) | ||
205 | "#]], | ||
206 | ); | ||
207 | } | ||
208 | |||
209 | #[test] | ||
192 | fn test_fn_signature_for_method_with_arg_as_assoc_fn() { | 210 | fn test_fn_signature_for_method_with_arg_as_assoc_fn() { |
193 | check( | 211 | check( |
194 | r#" | 212 | r#" |