Skip to content

Commit

Permalink
Handle desugaring in impl trait bound suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
wabain committed Dec 20, 2020
1 parent 1b6b06a commit b76c9be
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 13 deletions.
Expand Up @@ -254,27 +254,21 @@ fn suggest_restriction(
let pred = trait_ref.without_const().to_predicate(tcx).to_string();
let pred = pred.replace(&impl_trait_str, &type_param_name);
let mut sugg = vec![
// Find the last of the generic parameters contained within the span of
// the generics
match generics
.params
.iter()
.filter(|p| match p.kind {
hir::GenericParamKind::Type {
synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
..
} => false,
_ => true,
})
.last()
.map(|p| p.bounds_span().unwrap_or(p.span))
.filter(|&span| generics.span.contains(span) && span.desugaring_kind().is_none())
.max_by_key(|span| span.hi())
{
// `fn foo(t: impl Trait)`
// ^ suggest `<T: Trait>` here
None => (generics.span, format!("<{}>", type_param)),
// `fn foo<A>(t: impl Trait)`
// ^^^ suggest `<A, T: Trait>` here
Some(param) => (
param.bounds_span().unwrap_or(param.span).shrink_to_hi(),
format!(", {}", type_param),
),
Some(span) => (span.shrink_to_hi(), format!(", {}", type_param)),
},
// `fn foo(t: impl Trait)`
// ^ suggest `where <T as Trait>::A: Bound`
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/suggestions/impl-trait-with-missing-bounds.rs
Expand Up @@ -39,6 +39,14 @@ fn bak(constraints: impl Iterator + std::fmt::Debug) {
}
}

#[rustfmt::skip]
fn baw<>(constraints: impl Iterator) {
for constraint in constraints {
qux(constraint);
//~^ ERROR `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
}
}

fn qux(_: impl std::fmt::Debug) {}

fn main() {}
17 changes: 16 additions & 1 deletion src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr
Expand Up @@ -73,6 +73,21 @@ help: introduce a type parameter with a trait bound instead of using `impl Trait
LL | fn bak<I: Iterator + std::fmt::Debug>(constraints: I) where <I as Iterator>::Item: Debug {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 5 previous errors
error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
--> $DIR/impl-trait-with-missing-bounds.rs:45:13
|
LL | qux(constraint);
| ^^^^^^^^^^ `<impl Iterator as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug`
...
LL | fn qux(_: impl std::fmt::Debug) {}
| --------------- required by this bound in `qux`
|
= help: the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
help: introduce a type parameter with a trait bound instead of using `impl Trait`
|
LL | fn baw<I: Iterator>(constraints: I) where <I as Iterator>::Item: Debug {
| ^^^^^^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0277`.
@@ -0,0 +1,32 @@
// Regression test: if we suggest replacing an `impl Trait` argument to an async
// fn with a named type parameter in order to add bounds, the suggested function
// signature should be well-formed.
//
// edition:2018

trait Foo {
type Bar;
fn bar(&self) -> Self::Bar;
}

async fn run(_: &(), foo: impl Foo) -> std::io::Result<()> {
let bar = foo.bar();
assert_is_send(&bar);
//~^ ERROR: `<impl Foo as Foo>::Bar` cannot be sent between threads safely

Ok(())
}

// Test our handling of cases where there is a generic parameter list in the
// source, but only synthetic generic parameters
async fn run2< >(_: &(), foo: impl Foo) -> std::io::Result<()> {
let bar = foo.bar();
assert_is_send(&bar);
//~^ ERROR: `<impl Foo as Foo>::Bar` cannot be sent between threads safely

Ok(())
}

fn assert_is_send<T: Send>(_: &T) {}

fn main() {}
@@ -0,0 +1,33 @@
error[E0277]: `<impl Foo as Foo>::Bar` cannot be sent between threads safely
--> $DIR/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs:14:20
|
LL | assert_is_send(&bar);
| ^^^^ `<impl Foo as Foo>::Bar` cannot be sent between threads safely
...
LL | fn assert_is_send<T: Send>(_: &T) {}
| ---- required by this bound in `assert_is_send`
|
= help: the trait `Send` is not implemented for `<impl Foo as Foo>::Bar`
help: introduce a type parameter with a trait bound instead of using `impl Trait`
|
LL | async fn run<F: Foo>(_: &(), foo: F) -> std::io::Result<()> where <F as Foo>::Bar: Send {
| ^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0277]: `<impl Foo as Foo>::Bar` cannot be sent between threads safely
--> $DIR/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs:24:20
|
LL | assert_is_send(&bar);
| ^^^^ `<impl Foo as Foo>::Bar` cannot be sent between threads safely
...
LL | fn assert_is_send<T: Send>(_: &T) {}
| ---- required by this bound in `assert_is_send`
|
= help: the trait `Send` is not implemented for `<impl Foo as Foo>::Bar`
help: introduce a type parameter with a trait bound instead of using `impl Trait`
|
LL | async fn run2<F: Foo>(_: &(), foo: F) -> std::io::Result<()> where <F as Foo>::Bar: Send {
| ^^^^^^^^ ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0277`.

0 comments on commit b76c9be

Please sign in to comment.