Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/infer/unify.rs21
-rw-r--r--crates/hir-ty/src/lib.rs73
-rw-r--r--crates/hir-ty/src/traits.rs8
-rw-r--r--crates/hir/src/lib.rs44
-rw-r--r--crates/hir/src/source_analyzer.rs3
-rw-r--r--crates/ide-db/src/active_parameter.rs5
-rw-r--r--crates/ide/src/call_hierarchy.rs8
-rw-r--r--crates/ide/src/signature_help.rs102
8 files changed, 196 insertions, 68 deletions
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index b68fefc515..36e3a45889 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/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/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 1727cec989..26a839f0e9 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/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<TraitEnvironment>,
+pub fn callable_sig_from_fn_trait(
+ self_ty: &Ty,
+ trait_env: Arc<TraitEnvironment>,
db: &dyn HirDatabase,
-) -> Option<CallableSig> {
- 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<?args_ty>
// - <Self as FnOnce<?args_ty>>::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<chalk_ir::Goal<Interner>> = 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/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 7266cbb7b2..02f2cd7615 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/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/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index beb97d5458..82d2bbb6cf 100644
--- a/crates/hir/src/lib.rs
+++ b/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/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 057b03baef..d229584064 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -307,7 +307,8 @@ impl SourceAnalyzer {
db: &dyn HirDatabase,
call: &ast::Expr,
) -> Option<Callable> {
- 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/crates/ide-db/src/active_parameter.rs b/crates/ide-db/src/active_parameter.rs
index abc60a77a5..98d2e81754 100644
--- a/crates/ide-db/src/active_parameter.rs
+++ b/crates/ide-db/src/active_parameter.rs
@@ -60,10 +60,7 @@ pub fn callable_for_node(
token: &SyntaxToken,
) -> Option<(hir::Callable, Option<usize>)> {
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/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 458b852e2a..654a1cd316 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/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/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 96301ea0ce..378a38892c 100644
--- a/crates/ide/src/signature_help.rs
+++ b/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,13 +1428,82 @@ 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#"
+ <S as Fn>::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#"
+ <S as Fn>::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(
r#"
@@ -1828,19 +1908,19 @@ fn f<F: FnOnce(u8, u16) -> i32>(f: F) {
}
"#,
expect![[r#"
- (u8, u16) -> i32
- ^^ ---
+ impl FnOnce(u8, u16) -> i32
+ ^^ ---
"#]],
);
check(
r#"
-fn f<T, F: FnOnce(&T, u16) -> &T>(f: F) {
+fn f<T, F: FnMut(&T, u16) -> &T>(f: F) {
f($0)
}
"#,
expect![[r#"
- (&T, u16) -> &T
- ^^ ---
+ impl FnMut(&T, u16) -> &T
+ ^^ ---
"#]],
);
}
@@ -1860,7 +1940,7 @@ fn take<C, Error>(
}
"#,
expect![[r#"
- () -> i32
+ impl Fn() -> i32
"#]],
);
}