Skip to content

Commit 54d7728

Browse files
authored
Rollup merge of #76695 - iximeow:trait-generic-bound-suggestion, r=estebank
fix syntax error in suggesting generic constraint in trait parameter suggest `where T: Foo` for the first bound on a trait, then suggest `, T: Foo` when the suggested bound would add to an existing set of `where` clauses. `where T: Foo` may be the first bound if `T` has a default, because we'd rather suggest ``` trait A<T=()> where T: Copy ``` than ``` trait A<T: Copy=()> ``` for legibility reasons. the test case i added here is derived from [this reproduction](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=0bf3ace9f2a183d0bdbd748c6b8e3971): ``` struct B<T: Copy> { t: T } trait A<T = ()> { fn returns_constrained_type(&self, t: T) -> B<T> { B { t } } } ``` where the suggested fix, ``` trait A<T = ()>, T: Copy { ... } ``` is in fact invalid syntax! i also found an error in the existing suggestion for `trait Base<T = String>: Super<T>` where rustc would suggest `trait Base<T = String>: Super<T>, T: Copy`, but `T: Copy` is the first of the trait's `where` clauses and should be `where T: Copy` as well. the test for that suggestion expects invalid syntax, and has been revised to a compiler-pleasing `trait Base<T = String>: Super<T> where T: Copy`. judging by #70009 i'll.. cc @estebank ?
2 parents 2c2f1c2 + e1607c8 commit 54d7728

File tree

5 files changed

+105
-22
lines changed

5 files changed

+105
-22
lines changed

compiler/rustc_middle/src/ty/diagnostics.rs

+46-20
Original file line numberDiff line numberDiff line change
@@ -202,33 +202,59 @@ pub fn suggest_constraining_type_param(
202202
// Suggestion:
203203
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
204204
// - insert: `, T: Zar`
205+
//
206+
// Additionally, there may be no `where` clause whatsoever in the case that this was
207+
// reached because the generic parameter has a default:
208+
//
209+
// Message:
210+
// trait Foo<T=()> {... }
211+
// - help: consider further restricting this type parameter with `where T: Zar`
212+
//
213+
// Suggestion:
214+
// trait Foo<T=()> where T: Zar {... }
215+
// - insert: `where T: Zar`
205216

206-
let mut param_spans = Vec::new();
217+
if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
218+
&& generics.where_clause.predicates.len() == 0
219+
{
220+
// Suggest a bound, but there is no existing `where` clause *and* the type param has a
221+
// default (`<T=Foo>`), so we suggest adding `where T: Bar`.
222+
err.span_suggestion_verbose(
223+
generics.where_clause.tail_span_for_suggestion(),
224+
&msg_restrict_type_further,
225+
format!(" where {}: {}", param_name, constraint),
226+
Applicability::MachineApplicable,
227+
);
228+
} else {
229+
let mut param_spans = Vec::new();
207230

208-
for predicate in generics.where_clause.predicates {
209-
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
210-
span, bounded_ty, ..
211-
}) = predicate
212-
{
213-
if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
214-
if let Some(segment) = path.segments.first() {
215-
if segment.ident.to_string() == param_name {
216-
param_spans.push(span);
231+
for predicate in generics.where_clause.predicates {
232+
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
233+
span,
234+
bounded_ty,
235+
..
236+
}) = predicate
237+
{
238+
if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
239+
if let Some(segment) = path.segments.first() {
240+
if segment.ident.to_string() == param_name {
241+
param_spans.push(span);
242+
}
217243
}
218244
}
219245
}
220246
}
221-
}
222247

223-
match &param_spans[..] {
224-
&[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
225-
_ => {
226-
err.span_suggestion_verbose(
227-
generics.where_clause.tail_span_for_suggestion(),
228-
&msg_restrict_type_further,
229-
format!(", {}: {}", param_name, constraint),
230-
Applicability::MachineApplicable,
231-
);
248+
match &param_spans[..] {
249+
&[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
250+
_ => {
251+
err.span_suggestion_verbose(
252+
generics.where_clause.tail_span_for_suggestion(),
253+
&msg_restrict_type_further,
254+
format!(", {}: {}", param_name, constraint),
255+
Applicability::MachineApplicable,
256+
);
257+
}
232258
}
233259
}
234260

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// run-rustfix
2+
3+
#[allow(unused)]
4+
use std::fmt::Debug;
5+
// Rustfix should add this, or use `std::fmt::Debug` instead.
6+
7+
#[allow(dead_code)]
8+
struct ConstrainedStruct<X: Copy> {
9+
x: X
10+
}
11+
12+
#[allow(dead_code)]
13+
trait InsufficientlyConstrainedGeneric<X=()> where X: Copy {
14+
fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct<X> {
15+
//~^ ERROR the trait bound `X: Copy` is not satisfied
16+
ConstrainedStruct { x }
17+
}
18+
}
19+
20+
pub fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// run-rustfix
2+
3+
#[allow(unused)]
4+
use std::fmt::Debug;
5+
// Rustfix should add this, or use `std::fmt::Debug` instead.
6+
7+
#[allow(dead_code)]
8+
struct ConstrainedStruct<X: Copy> {
9+
x: X
10+
}
11+
12+
#[allow(dead_code)]
13+
trait InsufficientlyConstrainedGeneric<X=()> {
14+
fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct<X> {
15+
//~^ ERROR the trait bound `X: Copy` is not satisfied
16+
ConstrainedStruct { x }
17+
}
18+
}
19+
20+
pub fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0277]: the trait bound `X: Copy` is not satisfied
2+
--> $DIR/trait-impl-bound-suggestions.rs:14:52
3+
|
4+
LL | struct ConstrainedStruct<X: Copy> {
5+
| ---- required by this bound in `ConstrainedStruct`
6+
...
7+
LL | fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct<X> {
8+
| ^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `X`
9+
|
10+
help: consider further restricting type parameter `X`
11+
|
12+
LL | trait InsufficientlyConstrainedGeneric<X=()> where X: Copy {
13+
| ^^^^^^^^^^^^^
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0277`.

src/test/ui/type/type-check-defaults.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ LL | trait Base<T = String>: Super<T> { }
5656
|
5757
help: consider further restricting type parameter `T`
5858
|
59-
LL | trait Base<T = String>: Super<T>, T: Copy { }
60-
| ^^^^^^^^^
59+
LL | trait Base<T = String>: Super<T> where T: Copy { }
60+
| ^^^^^^^^^^^^^
6161

6262
error[E0277]: cannot add `u8` to `i32`
6363
--> $DIR/type-check-defaults.rs:24:66

0 commit comments

Comments
 (0)