From 6438554bce9ad9cc378986d89ff0bea0cebd239b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 18 May 2024 16:22:59 +0200 Subject: [PATCH] Show fn traits in signature info for trait implementors --- .../crates/hir-ty/src/infer/unify.rs | 21 ++-- .../rust-analyzer/crates/hir-ty/src/lib.rs | 73 +++++++++---- .../rust-analyzer/crates/hir-ty/src/traits.rs | 8 ++ src/tools/rust-analyzer/crates/hir/src/lib.rs | 44 ++++---- .../crates/hir/src/source_analyzer.rs | 3 +- .../crates/ide-db/src/active_parameter.rs | 5 +- .../crates/ide/src/call_hierarchy.rs | 8 +- .../crates/ide/src/signature_help.rs | 102 ++++++++++++++++-- 8 files changed, 196 insertions(+), 68 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index b68fefc5150b1..36e3a458898da 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -797,19 +797,22 @@ impl<'a> InferenceTable<'a> { }) .build(); - let projection = { - let b = TyBuilder::subst_for_def(self.db, fn_once_trait, None); - if b.remaining() != 2 { - return None; - } - let fn_once_subst = b.push(ty.clone()).push(arg_ty).build(); + let b = TyBuilder::trait_ref(self.db, fn_once_trait); + if b.remaining() != 2 { + return None; + } + let mut trait_ref = b.push(ty.clone()).push(arg_ty).build(); - TyBuilder::assoc_type_projection(self.db, output_assoc_type, Some(fn_once_subst)) - .build() + let projection = { + TyBuilder::assoc_type_projection( + self.db, + output_assoc_type, + Some(trait_ref.substitution.clone()), + ) + .build() }; let trait_env = self.trait_env.env.clone(); - let mut trait_ref = projection.trait_ref(self.db); let obligation = InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 1727cec9893b2..26a839f0e9fae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -570,6 +570,10 @@ impl CallableSig { } } + pub fn abi(&self) -> FnAbi { + self.abi + } + pub fn params(&self) -> &[Ty] { &self.params_and_return[0..self.params_and_return.len() - 1] } @@ -892,20 +896,16 @@ where Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) } } -pub fn callable_sig_from_fnonce( - mut self_ty: &Ty, - env: Arc, +pub fn callable_sig_from_fn_trait( + self_ty: &Ty, + trait_env: Arc, db: &dyn HirDatabase, -) -> Option { - if let Some((ty, _, _)) = self_ty.as_reference() { - // This will happen when it implements fn or fn mut, since we add a autoborrow adjustment - self_ty = ty; - } - let krate = env.krate; +) -> Option<(FnTrait, CallableSig)> { + let krate = trait_env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; - let mut table = InferenceTable::new(db, env); + let mut table = InferenceTable::new(db, trait_env.clone()); let b = TyBuilder::trait_ref(db, fn_once_trait); if b.remaining() != 2 { return None; @@ -915,23 +915,56 @@ pub fn callable_sig_from_fnonce( // - Self: FnOnce // - >::Output == ?ret_ty let args_ty = table.new_type_var(); - let trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build(); + let mut trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build(); let projection = TyBuilder::assoc_type_projection( db, output_assoc_type, Some(trait_ref.substitution.clone()), ) .build(); - table.register_obligation(trait_ref.cast(Interner)); - let ret_ty = table.normalize_projection_ty(projection); - - let ret_ty = table.resolve_completely(ret_ty); - let args_ty = table.resolve_completely(args_ty); - let params = - args_ty.as_tuple()?.iter(Interner).map(|it| it.assert_ty_ref(Interner)).cloned().collect(); - - Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe, FnAbi::RustCall)) + let block = trait_env.block; + let trait_env = trait_env.env.clone(); + let obligation = + InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() }; + let canonical = table.canonicalize(obligation.clone()); + if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() { + table.register_obligation(obligation.goal); + let return_ty = table.normalize_projection_ty(projection); + for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { + let fn_x_trait = fn_x.get_id(db, krate)?; + trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); + let obligation: chalk_ir::InEnvironment> = InEnvironment { + goal: trait_ref.clone().cast(Interner), + environment: trait_env.clone(), + }; + let canonical = table.canonicalize(obligation.clone()); + if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() { + let ret_ty = table.resolve_completely(return_ty); + let args_ty = table.resolve_completely(args_ty); + let params = args_ty + .as_tuple()? + .iter(Interner) + .map(|it| it.assert_ty_ref(Interner)) + .cloned() + .collect(); + + return Some(( + fn_x, + CallableSig::from_params_and_return( + params, + ret_ty, + false, + Safety::Safe, + FnAbi::RustCall, + ), + )); + } + } + unreachable!("It should at least implement FnOnce at this point"); + } else { + None + } } struct PlaceholderCollector<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 7266cbb7b2223..02f2cd761599a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -221,6 +221,14 @@ impl fmt::Display for FnTrait { } impl FnTrait { + pub const fn function_name(&self) -> &'static str { + match self { + FnTrait::FnOnce => "call_once", + FnTrait::FnMut => "call_mut", + FnTrait::Fn => "call", + } + } + const fn lang_item(self) -> LangItem { match self { FnTrait::FnOnce => LangItem::FnOnce, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index beb97d545850d..82d2bbb6cf314 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -140,7 +140,7 @@ pub use { display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, layout::LayoutError, mir::{MirEvalError, MirLowerError}, - PointerCast, Safety, + FnAbi, PointerCast, Safety, }, // FIXME: Properly encapsulate mir hir_ty::{mir, Interner as ChalkTyInterner}, @@ -2227,7 +2227,7 @@ impl Param { let InFile { file_id, value } = Function { id: func }.source(db)?; let params = value.param_list()?; if let Some(self_param) = params.self_param() { - if let Some(idx) = self.idx.checked_sub(1 as usize) { + if let Some(idx) = self.idx.checked_sub(1) { params.params().nth(idx).map(Either::Right) } else { Some(Either::Left(self_param)) @@ -4321,23 +4321,26 @@ impl Type { TyKind::Function(_) => Callee::FnPtr, TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?), kind => { - // This branch shouldn't be necessary? - if let TyKind::Ref(_, _, ty) = kind { - if let TyKind::Closure(closure, subst) = ty.kind(Interner) { - let sig = ty.callable_sig(db)?; - return Some(Callable { - ty: self.clone(), - sig, - callee: Callee::Closure(*closure, subst.clone()), - is_bound_method: false, - }); - } + // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment + let (ty, kind) = if let TyKind::Ref(_, _, ty) = kind { + (ty, ty.kind(Interner)) + } else { + (&self.ty, kind) + }; + if let TyKind::Closure(closure, subst) = kind { + let sig = ty.callable_sig(db)?; + return Some(Callable { + ty: self.clone(), + sig, + callee: Callee::Closure(*closure, subst.clone()), + is_bound_method: false, + }); } - let sig = hir_ty::callable_sig_from_fnonce(&self.ty, self.env.clone(), db)?; + let (fn_trait, sig) = hir_ty::callable_sig_from_fn_trait(ty, self.env.clone(), db)?; return Some(Callable { ty: self.clone(), sig, - callee: Callee::Other, + callee: Callee::FnImpl(fn_trait), is_bound_method: false, }); } @@ -4968,7 +4971,7 @@ enum Callee { Def(CallableDefId), Closure(ClosureId, Substitution), FnPtr, - Other, + FnImpl(FnTrait), } pub enum CallableKind { @@ -4977,8 +4980,7 @@ pub enum CallableKind { TupleEnumVariant(Variant), Closure(Closure), FnPtr, - /// Some other type that implements `FnOnce`. - Other, + FnImpl(FnTrait), } impl Callable { @@ -4993,7 +4995,7 @@ impl Callable { CallableKind::Closure(Closure { id, subst: subst.clone() }) } Callee::FnPtr => CallableKind::FnPtr, - Callee::Other => CallableKind::Other, + Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_), } } pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(SelfParam, Type)> { @@ -5023,6 +5025,10 @@ impl Callable { pub fn sig(&self) -> &CallableSig { &self.sig } + + pub fn ty(&self) -> &Type { + &self.ty + } } #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 057b03baef07a..d22958406420e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -307,7 +307,8 @@ impl SourceAnalyzer { db: &dyn HirDatabase, call: &ast::Expr, ) -> Option { - self.type_of_expr(db, &call.clone())?.0.as_callable(db) + let (orig, adjusted) = self.type_of_expr(db, &call.clone())?; + adjusted.unwrap_or(orig).as_callable(db) } pub(crate) fn resolve_field( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs index abc60a77a5642..98d2e81754611 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs @@ -60,10 +60,7 @@ pub fn callable_for_node( token: &SyntaxToken, ) -> Option<(hir::Callable, Option)> { let callable = match calling_node { - ast::CallableExpr::Call(call) => { - let expr = call.expr()?; - sema.type_of_expr(&expr)?.adjusted().as_callable(sema.db) - } + ast::CallableExpr::Call(call) => sema.resolve_expr_as_callable(&call.expr()?), ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call), }?; let active_param = calling_node.arg_list().map(|arg_list| { diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index 458b852e2a1e7..654a1cd316475 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -109,12 +109,12 @@ pub(crate) fn outgoing_calls( let expr = call.expr()?; let callable = sema.type_of_expr(&expr)?.original.as_callable(db)?; match callable.kind() { - hir::CallableKind::Function(it) => { - let range = expr.syntax().text_range(); - it.try_to_nav(db).zip(Some(range)) - } + hir::CallableKind::Function(it) => it.try_to_nav(db), + hir::CallableKind::TupleEnumVariant(it) => it.try_to_nav(db), + hir::CallableKind::TupleStruct(it) => it.try_to_nav(db), _ => None, } + .zip(Some(expr.syntax().text_range())) } ast::CallableExpr::MethodCall(expr) => { let range = expr.name_ref()?.syntax().text_range(); diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 96301ea0cee2f..378a38892c7df 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -202,9 +202,20 @@ fn signature_help_for_call( ); } hir::CallableKind::Closure(closure) => { - format_to!(res.signature, "impl {}", closure.fn_trait(db)); + let fn_trait = closure.fn_trait(db); + format_to!(res.signature, "impl {fn_trait}") } - hir::CallableKind::FnPtr | hir::CallableKind::Other => (), + hir::CallableKind::FnPtr => format_to!(res.signature, "fn"), + hir::CallableKind::FnImpl(fn_trait) => match callable.ty().as_adt() { + // FIXME: Render docs of the concrete trait impl function + Some(adt) => format_to!( + res.signature, + "<{} as {fn_trait}>::{}", + adt.name(db).display(db), + fn_trait.function_name() + ), + None => format_to!(res.signature, "impl {fn_trait}"), + }, } res.signature.push('('); @@ -250,7 +261,7 @@ fn signature_help_for_call( hir::CallableKind::Function(_) | hir::CallableKind::Closure(_) | hir::CallableKind::FnPtr - | hir::CallableKind::Other => render(callable.return_type()), + | hir::CallableKind::FnImpl(_) => render(callable.return_type()), hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {} } Some(res) @@ -1417,12 +1428,81 @@ fn main(f: fn(i32, f64) -> char) { } "#, expect![[r#" - (i32, f64) -> char - --- ^^^ + fn(i32, f64) -> char + --- ^^^ "#]], ) } + #[test] + fn call_info_for_fn_impl() { + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnMut<(i32, f64)> for S {} +impl core::ops::Fn<(i32, f64)> for S {} +fn main() { + S($0); +} + "#, + expect![[r#" + ::call(i32, f64) -> char + ^^^ --- + "#]], + ); + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnMut<(i32, f64)> for S {} +impl core::ops::Fn<(i32, f64)> for S {} +fn main() { + S(1, $0); +} + "#, + expect![[r#" + ::call(i32, f64) -> char + --- ^^^ + "#]], + ); + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnOnce<(char, char)> for S { + type Output = f64; +} +fn main() { + S($0); +} + "#, + expect![""], + ); + check( + r#" +struct S; +impl core::ops::FnOnce<(i32, f64)> for S { + type Output = char; +} +impl core::ops::FnOnce<(char, char)> for S { + type Output = f64; +} +fn main() { + // FIXME: The ide layer loses the calling info here so we get an ambiguous trait solve result + S(0i32, $0); +} + "#, + expect![""], + ); + } + #[test] fn call_info_for_unclosed_call() { check( @@ -1828,19 +1908,19 @@ fn f i32>(f: F) { } "#, expect![[r#" - (u8, u16) -> i32 - ^^ --- + impl FnOnce(u8, u16) -> i32 + ^^ --- "#]], ); check( r#" -fn f &T>(f: F) { +fn f &T>(f: F) { f($0) } "#, expect![[r#" - (&T, u16) -> &T - ^^ --- + impl FnMut(&T, u16) -> &T + ^^ --- "#]], ); } @@ -1860,7 +1940,7 @@ fn take( } "#, expect![[r#" - () -> i32 + impl Fn() -> i32 "#]], ); }