Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::fmt;

use rustc_data_structures::intern::Interned;
use rustc_errors::{Diag, IntoDiagArg};
use rustc_errors::{Applicability, Diag, IntoDiagArg};
use rustc_hir as hir;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_middle::bug;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::print::{FmtPrinter, Print, PrintTraitRefExt as _, RegionHighlightMode};
use rustc_middle::ty::{self, GenericArgsRef, RePlaceholder, Region, TyCtxt};
use rustc_middle::ty::{self, GenericArgsRef, IsSuggestable, RePlaceholder, Region, TyCtxt};
use tracing::{debug, instrument};

use crate::diagnostics::{
Expand Down Expand Up @@ -379,6 +380,50 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
}
}

// When the mismatched trait is an Fn-trait and the self type is a closure with
// unannotated parameters, suggest adding explicit type annotations. This turns
// the confusing lifetime-generality error into an actionable hint, e.g.:
// |buf| → |buf: &mut [u8]|
if self.tcx().is_fn_trait(trait_def_id) {
let actual_self_ty = self.cx.resolve_vars_if_possible(
ty::TraitRef::new_from_args(self.cx.tcx, trait_def_id, actual_args).self_ty(),
);
if let ty::Closure(closure_def_id, _) = *actual_self_ty.kind()
&& let Some(local_def_id) = closure_def_id.as_local()
&& let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) =
self.tcx().hir_node_by_def_id(local_def_id)
{
let body = self.tcx().hir_body(closure.body);
// For Fn traits, args[1] is the tupled input types (e.g. `(&mut [u8],)`).
let expected_input_tys = expected_args.type_at(1);
if let ty::Tuple(input_tys) = *expected_input_tys.kind() {
let suggestions: Vec<_> = body
.params
.iter()
.zip(input_tys.iter())
.filter_map(|(param, ty)| {
// ty_span == pat.span means no explicit type annotation was written.
if param.ty_span == param.pat.span
&& ty.is_suggestable(self.tcx(), false)
{
Some((param.pat.span.shrink_to_hi(), format!(": {ty}")))
} else {
None
}
})
.collect();
if !suggestions.is_empty() {
let msg = if suggestions.len() == 1 {
"consider adding an explicit type annotation to the closure's argument"
} else {
"consider adding explicit type annotations to the closure's arguments"
};
err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
}
}
}
}

err
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ LL | require_send(future);
|
= note: closure with signature `fn(&'0 Vec<i32>) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec<i32>,)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&Vec<i32>,)>`
help: consider adding an explicit type annotation to the closure's argument
|
LL | for _ in things.iter().map(|n: &Vec<i32>| n.iter()).flatten() {
| +++++++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/higher-ranked-auto-trait-15.rs:20:5
Expand All @@ -16,6 +20,10 @@ LL | require_send(future);
= note: closure with signature `fn(&'0 Vec<i32>) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec<i32>,)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&Vec<i32>,)>`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure's argument
|
LL | for _ in things.iter().map(|n: &Vec<i32>| n.iter()).flatten() {
| +++++++++++

error: aborting due to 2 previous errors

44 changes: 44 additions & 0 deletions tests/ui/closures/hrtb-closure-suggest-type-annotation.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//@ run-rustfix
// When a closure is passed where a higher-ranked `FnOnce` is expected, but the closure
// parameter has no explicit type annotation, the compiler should suggest adding one.
// See <https://github.com/rust-lang/rust/issues/141461>.

fn inner<F>(buf: &mut [u8], func: F) -> usize
where
F: FnOnce(&mut [u8]) -> Result<usize, isize>,
{
match func(buf) {
Ok(n) => n,
Err(e) => e as usize,
}
}

fn outer<F>(buf: &mut [u8], func: F) -> usize
where
F: FnOnce(&mut [u8]) -> Result<usize, isize>,
{
let outer_closure = |buf: &mut [u8]| {
match func(buf) {
Ok(n) => {
println!("OK: {n}");
Ok(n)
}
Err(e) => {
println!("ERR: {e}");
Err(e)
}
}
};

inner(buf, outer_closure)
//~^ ERROR implementation of `FnOnce` is not general enough
//~| ERROR implementation of `FnOnce` is not general enough
}

fn main() {
let mut x = [0u8; 16];
let res = outer(&mut x, |buf| {
if buf.len() % 2 == 0 { Ok(buf.len()) } else { Err(buf.len() as isize) }
});
println!("{res:?}");
}
44 changes: 44 additions & 0 deletions tests/ui/closures/hrtb-closure-suggest-type-annotation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//@ run-rustfix
// When a closure is passed where a higher-ranked `FnOnce` is expected, but the closure
// parameter has no explicit type annotation, the compiler should suggest adding one.
// See <https://github.com/rust-lang/rust/issues/141461>.

fn inner<F>(buf: &mut [u8], func: F) -> usize
where
F: FnOnce(&mut [u8]) -> Result<usize, isize>,
{
match func(buf) {
Ok(n) => n,
Err(e) => e as usize,
}
}

fn outer<F>(buf: &mut [u8], func: F) -> usize
where
F: FnOnce(&mut [u8]) -> Result<usize, isize>,
{
let outer_closure = |buf| {
match func(buf) {
Ok(n) => {
println!("OK: {n}");
Ok(n)
}
Err(e) => {
println!("ERR: {e}");
Err(e)
}
}
};

inner(buf, outer_closure)
//~^ ERROR implementation of `FnOnce` is not general enough
//~| ERROR implementation of `FnOnce` is not general enough
}

fn main() {
let mut x = [0u8; 16];
let res = outer(&mut x, |buf| {
if buf.len() % 2 == 0 { Ok(buf.len()) } else { Err(buf.len() as isize) }
});
println!("{res:?}");
}
29 changes: 29 additions & 0 deletions tests/ui/closures/hrtb-closure-suggest-type-annotation.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error: implementation of `FnOnce` is not general enough
--> $DIR/hrtb-closure-suggest-type-annotation.rs:33:5
|
LL | inner(buf, outer_closure)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `fn(&'2 mut [u8]) -> Result<usize, isize>` must implement `FnOnce<(&'1 mut [u8],)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 mut [u8],)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure's argument
|
LL | let outer_closure = |buf: &mut [u8]| {
| +++++++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/hrtb-closure-suggest-type-annotation.rs:33:5
|
LL | inner(buf, outer_closure)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `fn(&'2 mut [u8]) -> Result<usize, isize>` must implement `FnOnce<(&'1 mut [u8],)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 mut [u8],)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure's argument
|
LL | let outer_closure = |buf: &mut [u8]| {
| +++++++++++

error: aborting due to 2 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ LL | foo(bar, "string", |s| s.len() == 5);
|
= note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure's argument
|
LL | foo(bar, "string", |s: &&str| s.len() == 5);
| +++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-71955.rs:45:5
Expand All @@ -16,6 +20,10 @@ LL | foo(bar, "string", |s| s.len() == 5);
= note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure's argument
|
LL | foo(bar, "string", |s: &&str| s.len() == 5);
| +++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-71955.rs:48:5
Expand All @@ -25,6 +33,10 @@ LL | foo(baz, "string", |s| s.0.len() == 5);
|
= note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure's argument
|
LL | foo(baz, "string", |s: &Wrapper<'_>| s.0.len() == 5);
| ++++++++++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-71955.rs:48:5
Expand All @@ -35,6 +47,10 @@ LL | foo(baz, "string", |s| s.0.len() == 5);
= note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure's argument
|
LL | foo(baz, "string", |s: &Wrapper<'_>| s.0.len() == 5);
| ++++++++++++++

error: aborting due to 4 previous errors

24 changes: 24 additions & 0 deletions tests/ui/lifetimes/issue-105675.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ LL | thing(f);
|
= note: closure with signature `for<'a> fn(&'2 u32, &'a u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2`
help: consider adding explicit type annotations to the closure's arguments
|
LL | let f = | _: &u32 , y: &u32 , z: u32 | ();
| ++++++ +++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-105675.rs:5:5
Expand All @@ -16,6 +20,10 @@ LL | thing(f);
= note: closure with signature `for<'a> fn(&'2 u32, &'a u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding explicit type annotations to the closure's arguments
|
LL | let f = | _: &u32 , y: &u32 , z: u32 | ();
| ++++++ +++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-105675.rs:9:5
Expand All @@ -25,6 +33,10 @@ LL | thing(f);
|
= note: closure with signature `fn(&'2 u32, &u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure's argument
|
LL | let f = | x: &u32, y: _ , z: u32 | ();
| ++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-105675.rs:9:5
Expand All @@ -34,6 +46,10 @@ LL | thing(f);
|
= note: closure with signature `fn(&u32, &'2 u32, u32)` must implement `FnOnce<(&u32, &'1 u32, u32)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&u32, &'2 u32, u32)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure's argument
|
LL | let f = | x: &u32, y: _ , z: u32 | ();
| ++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-105675.rs:9:5
Expand All @@ -44,6 +60,10 @@ LL | thing(f);
= note: closure with signature `fn(&'2 u32, &u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure's argument
|
LL | let f = | x: &u32, y: _ , z: u32 | ();
| ++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-105675.rs:9:5
Expand All @@ -54,6 +74,10 @@ LL | thing(f);
= note: closure with signature `fn(&u32, &'2 u32, u32)` must implement `FnOnce<(&u32, &'1 u32, u32)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&u32, &'2 u32, u32)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure's argument
|
LL | let f = | x: &u32, y: _ , z: u32 | ();
| ++++++

error: aborting due to 6 previous errors

8 changes: 8 additions & 0 deletions tests/ui/lifetimes/issue-79187-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ LL | take_foo(|a| a);
|
= note: closure with signature `fn(&'2 i32) -> &i32` must implement `FnOnce<(&'1 i32,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 i32,)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure's argument
|
LL | take_foo(|a: &i32| a);
| ++++++

error: implementation of `Fn` is not general enough
--> $DIR/issue-79187-2.rs:8:5
Expand All @@ -33,6 +37,10 @@ LL | take_foo(|a| a);
|
= note: closure with signature `fn(&'2 i32) -> &i32` must implement `Fn<(&'1 i32,)>`, for any lifetime `'1`...
= note: ...but it actually implements `Fn<(&'2 i32,)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure's argument
|
LL | take_foo(|a: &i32| a);
| ++++++

error[E0308]: mismatched types
--> $DIR/issue-79187-2.rs:11:5
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/lifetimes/issue-79187.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ LL | thing(f);
|
= note: closure with signature `fn(&'2 u32)` must implement `FnOnce<(&'1 u32,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 u32,)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure's argument
|
LL | let f = |_: &u32| ();
| ++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-79187.rs:5:5
Expand All @@ -16,6 +20,10 @@ LL | thing(f);
= note: closure with signature `fn(&'2 u32)` must implement `FnOnce<(&'1 u32,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 u32,)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure's argument
|
LL | let f = |_: &u32| ();
| ++++++

error: aborting due to 2 previous errors

Loading
Loading