From 4a6cdd776d403bacce0a5471d77e8c76695c5bc5 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 23 May 2021 16:59:23 +0200 Subject: Record method call substs and use them in call info --- crates/hir/src/semantics.rs | 10 ++--- crates/hir/src/source_analyzer.rs | 2 +- crates/hir_ty/src/diagnostics/expr.rs | 12 ++---- crates/hir_ty/src/diagnostics/unsafe_check.rs | 2 +- crates/hir_ty/src/infer.rs | 25 ++++++----- crates/hir_ty/src/infer/expr.rs | 34 ++++++++------- crates/hir_ty/src/infer/unify.rs | 7 +++- crates/ide_completion/src/context.rs | 60 +++++++++++++++++++++++++++ crates/ide_db/src/call_info/tests.rs | 18 ++++++++ 9 files changed, 125 insertions(+), 45 deletions(-) (limited to 'crates') 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::{ AsMacroCall, FunctionId, TraitId, VariantId, }; use hir_expand::{name::AsName, ExpansionInfo}; -use hir_ty::associated_type_shorthand_candidates; +use hir_ty::{associated_type_shorthand_candidates, Interner}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ @@ -501,14 +501,12 @@ impl<'db> SemanticsImpl<'db> { } fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option { - self.analyze(call.syntax()).resolve_method_call(self.db, call) + self.analyze(call.syntax()).resolve_method_call(self.db, call).map(|(id, _)| id) } fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { - // FIXME: this erases Substs, we should instead record the correct - // substitution during inference and use that - let func = self.resolve_method_call(call)?; - let ty = hir_ty::TyBuilder::value_ty(self.db, func.into()).fill_with_unknown().build(); + let (func, subst) = self.analyze(call.syntax()).resolve_method_call(self.db, call)?; + let ty = self.db.value_ty(func.into()).substitute(&Interner, &subst); let resolver = self.analyze(call.syntax()).resolver; let ty = Type::new_with_resolver(self.db, &resolver, ty)?; 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 { &self, db: &dyn HirDatabase, call: &ast::MethodCallExpr, - ) -> Option { + ) -> Option<(FunctionId, Substitution)> { let expr_id = self.expr_id(db, &call.clone().into())?; self.infer.as_ref()?.method_resolution(expr_id) } 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> { for (id, expr) in body.exprs.iter() { if let Expr::MethodCall { receiver, .. } = expr { let function_id = match self.infer.method_resolution(id) { - Some(id) => id, + Some((id, _)) => id, None => continue, }; @@ -239,15 +239,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> { return; } - // FIXME: note that we erase information about substs here. This - // is not right, but, luckily, doesn't matter as we care only - // about the number of params - let callee = match self.infer.method_resolution(call_id) { - Some(callee) => callee, + let (callee, subst) = match self.infer.method_resolution(call_id) { + Some(it) => it, None => return, }; - let sig = - db.callable_item_signature(callee.into()).into_value_and_skipped_binders().0; + let sig = db.callable_item_signature(callee.into()).substitute(&Interner, &subst); (sig, args) } 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( Expr::MethodCall { .. } => { if infer .method_resolution(current) - .map(|func| db.function_data(func).is_unsafe()) + .map(|(func, _)| db.function_data(func).is_unsafe()) .unwrap_or(false) { 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; use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; use crate::{ db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, - lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, TyBuilder, - TyExt, TyKind, + lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, + TyBuilder, TyExt, TyKind, }; // This lint has a false positive here. See the link below for details. @@ -132,7 +132,7 @@ impl Default for InternedStandardTypes { #[derive(Clone, PartialEq, Eq, Debug, Default)] pub struct InferenceResult { /// For each method call expr, records the function it resolves to. - method_resolutions: FxHashMap, + method_resolutions: FxHashMap, /// For each field access expr, records the field it resolves to. field_resolutions: FxHashMap, /// For each struct literal or pattern, records the variant it resolves to. @@ -152,8 +152,8 @@ pub struct InferenceResult { } impl InferenceResult { - pub fn method_resolution(&self, expr: ExprId) -> Option { - self.method_resolutions.get(&expr).copied() + pub fn method_resolution(&self, expr: ExprId) -> Option<(FunctionId, Substitution)> { + self.method_resolutions.get(&expr).cloned() } pub fn field_resolution(&self, expr: ExprId) -> Option { self.field_resolutions.get(&expr).copied() @@ -284,14 +284,17 @@ impl<'a> InferenceContext<'a> { self.table.propagate_diverging_flag(); let mut result = std::mem::take(&mut self.result); for ty in result.type_of_expr.values_mut() { - *ty = self.table.resolve_ty_completely(ty.clone()); + *ty = self.table.resolve_completely(ty.clone()); } for ty in result.type_of_pat.values_mut() { - *ty = self.table.resolve_ty_completely(ty.clone()); + *ty = self.table.resolve_completely(ty.clone()); } for mismatch in result.type_mismatches.values_mut() { - mismatch.expected = self.table.resolve_ty_completely(mismatch.expected.clone()); - mismatch.actual = self.table.resolve_ty_completely(mismatch.actual.clone()); + mismatch.expected = self.table.resolve_completely(mismatch.expected.clone()); + mismatch.actual = self.table.resolve_completely(mismatch.actual.clone()); + } + for (_, subst) in result.method_resolutions.values_mut() { + *subst = self.table.resolve_completely(subst.clone()); } result } @@ -300,8 +303,8 @@ impl<'a> InferenceContext<'a> { self.result.type_of_expr.insert(expr, ty); } - fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId) { - self.result.method_resolutions.insert(expr, func); + fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) { + self.result.method_resolutions.insert(expr, (func, subst)); } 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> { method_name, ) }); - let (derefed_receiver_ty, method_ty, def_generics) = match resolved { + let (derefed_receiver_ty, method_ty, substs) = match resolved { Some((ty, func)) => { let ty = canonicalized_receiver.decanonicalize_ty(ty); - self.write_method_resolution(tgt_expr, func); - (ty, self.db.value_ty(func.into()), Some(generics(self.db.upcast(), func.into()))) + let generics = generics(self.db.upcast(), func.into()); + let substs = self.substs_for_method_call(generics, generic_args, &ty); + self.write_method_resolution(tgt_expr, func, substs.clone()); + (ty, self.db.value_ty(func.into()), substs) } - None => (receiver_ty, Binders::empty(&Interner, self.err_ty()), None), + None => ( + receiver_ty, + Binders::empty(&Interner, self.err_ty()), + Substitution::empty(&Interner), + ), }; - let substs = self.substs_for_method_call(def_generics, generic_args, &derefed_receiver_ty); let method_ty = method_ty.substitute(&Interner, &substs); - let method_ty = self.insert_type_vars(method_ty); self.register_obligations_for_call(&method_ty); let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) { Some(sig) => { @@ -950,23 +954,21 @@ impl<'a> InferenceContext<'a> { fn substs_for_method_call( &mut self, - def_generics: Option, + def_generics: Generics, generic_args: Option<&GenericArgs>, receiver_ty: &Ty, ) -> Substitution { let (parent_params, self_params, type_params, impl_trait_params) = - def_generics.as_ref().map_or((0, 0, 0, 0), |g| g.provenance_split()); + def_generics.provenance_split(); assert_eq!(self_params, 0); // method shouldn't have another Self param let total_len = parent_params + type_params + impl_trait_params; let mut substs = Vec::with_capacity(total_len); // Parent arguments are unknown, except for the receiver type - if let Some(parent_generics) = def_generics.as_ref().map(|p| p.iter_parent()) { - for (_id, param) in parent_generics { - if param.provenance == hir_def::generics::TypeParamProvenance::TraitSelf { - substs.push(receiver_ty.clone()); - } else { - substs.push(self.err_ty()); - } + for (_id, param) in def_generics.iter_parent() { + if param.provenance == hir_def::generics::TypeParamProvenance::TraitSelf { + substs.push(receiver_ty.clone()); + } else { + substs.push(self.table.new_type_var()); } } // handle provided type arguments @@ -989,7 +991,7 @@ impl<'a> InferenceContext<'a> { }; let supplied_params = substs.len(); for _ in supplied_params..total_len { - substs.push(self.err_ty()); + substs.push(self.table.new_type_var()); } assert_eq!(substs.len(), total_len); 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> { .expect("fold failed unexpectedly") } - pub(crate) fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { - self.resolve_with_fallback(ty, |_, _, d, _| d) + pub(crate) fn resolve_completely(&mut self, t: T) -> T::Result + where + T: HasInterner + Fold, + { + self.resolve_with_fallback(t, |_, _, d, _| d) } /// 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 @@ -784,6 +784,19 @@ fn foo() { ) } + #[test] + fn expected_type_generic_struct_field() { + check_expected_type_and_name( + r#" +struct Foo { a: T } +fn foo() -> Foo { + Foo { a: $0 } +} +"#, + expect![[r#"ty: u32, name: a"#]], + ) + } + #[test] fn expected_type_struct_field_with_leading_char() { cov_mark::check!(expected_type_struct_field_with_leading_char); @@ -895,4 +908,51 @@ fn foo() -> u32 { expect![[r#"ty: u32, name: ?"#]], ) } + + #[test] + fn expected_type_closure_param() { + check_expected_type_and_name( + r#" +fn foo() { + bar(|| $0); +} + +fn bar(f: impl FnOnce() -> u32) {} +#[lang = "fn_once"] +trait FnOnce { type Output; } +"#, + expect![[r#"ty: u32, name: ?"#]], + ); + } + + #[test] + fn expected_type_generic_function() { + check_expected_type_and_name( + r#" +fn foo() { + bar::($0); +} + +fn bar(t: T) {} +"#, + expect![[r#"ty: u32, name: t"#]], + ); + } + + #[test] + fn expected_type_generic_method() { + check_expected_type_and_name( + r#" +fn foo() { + S(1u32).bar($0); +} + +struct S(T); +impl S { + fn bar(self, t: T) {} +} +"#, + expect![[r#"ty: u32, name: t"#]], + ); + } } 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 @@ -188,6 +188,24 @@ fn main() { S.foo($0); } ); } +#[test] +fn test_fn_signature_for_generic_method() { + check( + r#" +struct S(T); +impl S { + fn foo(&self, x: T) {} +} + +fn main() { S(1u32).foo($0); } +"#, + expect![[r#" + fn foo(&self, x: u32) + () + "#]], + ); +} + #[test] fn test_fn_signature_for_method_with_arg_as_assoc_fn() { check( -- cgit v1.2.3