Skip to content

Commit f415c07

Browse files
committed
Auto merge of #133522 - estebank:dont-suggest-unstable-trait, r=compiler-errors
Don't suggest restricting bound with unstable traits on stable and mention it's unstable on nightly On nightly, we mention the trait is unstable ``` error[E0277]: the trait bound `T: Unstable` is not satisfied --> $DIR/unstable-trait-suggestion.rs:13:9 | LL | foo(t) | --- ^ the trait `Unstable` is not implemented for `T` | | | required by a bound introduced by this call | note: required by a bound in `foo` --> $DIR/unstable-trait-suggestion.rs:9:11 | LL | fn foo<T: Unstable>(_: T) {} | ^^^^^^^^ required by this bound in `foo` help: consider restricting type parameter `T` but it is an `unstable` trait | LL | pub fn demo<T: Unstable>(t: T) { | ++++++++++ ``` On stable, we don't suggest the trait at all ``` error[E0277]: the trait bound `T: Unstable` is not satisfied --> $DIR/unstable-trait-suggestion.rs:13:9 | LL | foo(t) | --- ^ the trait `Unstable` is not implemented for `T` | | | required by a bound introduced by this call | note: required by a bound in `foo` --> $DIR/unstable-trait-suggestion.rs:9:11 | LL | fn foo<T: Unstable>(_: T) {} | ^^^^^^^^ required by this bound in `foo` ``` Fix #133511.
2 parents 728f2da + 25ad047 commit f415c07

File tree

160 files changed

+486
-374
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

160 files changed

+486
-374
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1450,6 +1450,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
14501450
ty::Param(param_ty) => Ok((
14511451
generics.type_param(param_ty, tcx),
14521452
predicate.trait_ref.print_trait_sugared().to_string(),
1453+
Some(predicate.trait_ref.def_id),
14531454
)),
14541455
_ => Err(()),
14551456
}
@@ -1463,9 +1464,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
14631464
tcx,
14641465
hir_generics,
14651466
err,
1466-
predicates
1467-
.iter()
1468-
.map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
1467+
predicates.iter().map(|(param, constraint, def_id)| {
1468+
(param.name.as_str(), &**constraint, *def_id)
1469+
}),
14691470
None,
14701471
);
14711472
}

compiler/rustc_const_eval/src/check_consts/ops.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
140140
err,
141141
param_ty.name.as_str(),
142142
&constraint,
143-
None,
143+
Some(trait_ref.def_id),
144144
None,
145145
);
146146
}

compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
279279
} else {
280280
let mut err = self.dcx().create_err(err);
281281
if suggest_constraining_type_param(
282-
tcx, generics, &mut err, &qself_str, &trait_ref, None, None,
282+
tcx,
283+
generics,
284+
&mut err,
285+
&qself_str,
286+
&trait_ref,
287+
Some(best_trait),
288+
None,
283289
) && !identically_named
284290
{
285291
// We suggested constraining a type parameter, but the associated item on it

compiler/rustc_middle/src/ty/diagnostics.rs

+99-27
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
//! Diagnostics related methods for `Ty`.
22
3-
use std::borrow::Cow;
43
use std::fmt::Write;
54
use std::ops::ControlFlow;
65

76
use rustc_data_structures::fx::FxHashMap;
8-
use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display};
7+
use rustc_errors::{
8+
Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, pluralize,
9+
};
910
use rustc_hir::def::DefKind;
1011
use rustc_hir::def_id::DefId;
1112
use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind};
@@ -161,7 +162,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>(
161162
true
162163
}
163164

164-
#[derive(Debug)]
165+
#[derive(Debug, Clone, Copy)]
165166
enum SuggestChangingConstraintsMessage<'a> {
166167
RestrictBoundFurther,
167168
RestrictType { ty: &'a str },
@@ -172,7 +173,7 @@ enum SuggestChangingConstraintsMessage<'a> {
172173

173174
fn suggest_changing_unsized_bound(
174175
generics: &hir::Generics<'_>,
175-
suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
176+
suggestions: &mut Vec<(Span, String, String, SuggestChangingConstraintsMessage<'_>)>,
176177
param: &hir::GenericParam<'_>,
177178
def_id: Option<DefId>,
178179
) {
@@ -207,7 +208,8 @@ fn suggest_changing_unsized_bound(
207208
continue;
208209
}
209210

210-
let mut push_suggestion = |sp, msg| suggestions.push((sp, String::new(), msg));
211+
let mut push_suggestion =
212+
|sp, msg| suggestions.push((sp, "Sized".to_string(), String::new(), msg));
211213

212214
if predicate.bounds.len() == unsized_bounds.len() {
213215
// All the bounds are unsized bounds, e.g.
@@ -278,8 +280,25 @@ pub fn suggest_constraining_type_params<'a>(
278280
span_to_replace: Option<Span>,
279281
) -> bool {
280282
let mut grouped = FxHashMap::default();
283+
let mut unstable_suggestion = false;
281284
param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
282-
grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id))
285+
let stable = match def_id {
286+
Some(def_id) => match tcx.lookup_stability(def_id) {
287+
Some(s) => s.level.is_stable(),
288+
None => true,
289+
},
290+
None => true,
291+
};
292+
if stable || tcx.sess.is_nightly_build() {
293+
grouped.entry(param_name).or_insert(Vec::new()).push((
294+
constraint,
295+
def_id,
296+
if stable { "" } else { "unstable " },
297+
));
298+
if !stable {
299+
unstable_suggestion = true;
300+
}
301+
}
283302
});
284303

285304
let mut applicability = Applicability::MachineApplicable;
@@ -290,16 +309,21 @@ pub fn suggest_constraining_type_params<'a>(
290309
let Some(param) = param else { return false };
291310

292311
{
293-
let mut sized_constraints = constraints.extract_if(|(_, def_id)| {
312+
let mut sized_constraints = constraints.extract_if(|(_, def_id, _)| {
294313
def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized))
295314
});
296-
if let Some((_, def_id)) = sized_constraints.next() {
315+
if let Some((_, def_id, _)) = sized_constraints.next() {
297316
applicability = Applicability::MaybeIncorrect;
298317

299318
err.span_label(param.span, "this type parameter needs to be `Sized`");
300319
suggest_changing_unsized_bound(generics, &mut suggestions, param, def_id);
301320
}
302321
}
322+
let bound_message = if constraints.iter().any(|(_, def_id, _)| def_id.is_none()) {
323+
SuggestChangingConstraintsMessage::RestrictBoundFurther
324+
} else {
325+
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }
326+
};
303327

304328
// in the scenario like impl has stricter requirements than trait,
305329
// we should not suggest restrict bound on the impl, here we double check
@@ -312,15 +336,54 @@ pub fn suggest_constraining_type_params<'a>(
312336
.collect();
313337

314338
constraints
315-
.retain(|(_, def_id)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def)));
339+
.retain(|(_, def_id, _)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def)));
316340

317341
if constraints.is_empty() {
318342
continue;
319343
}
320344

321-
let mut constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>();
345+
let mut constraint = constraints.iter().map(|&(c, _, _)| c).collect::<Vec<_>>();
322346
constraint.sort();
323347
constraint.dedup();
348+
let all_known = constraints.iter().all(|&(_, def_id, _)| def_id.is_some());
349+
let all_stable = constraints.iter().all(|&(_, _, stable)| stable.is_empty());
350+
let all_unstable = constraints.iter().all(|&(_, _, stable)| !stable.is_empty());
351+
let post = if all_stable || all_unstable {
352+
// Don't redundantly say "trait `X`, trait `Y`", instead "traits `X` and `Y`"
353+
let mut trait_names = constraints
354+
.iter()
355+
.map(|&(c, def_id, _)| match def_id {
356+
None => format!("`{c}`"),
357+
Some(def_id) => format!("`{}`", tcx.item_name(def_id)),
358+
})
359+
.collect::<Vec<_>>();
360+
trait_names.sort();
361+
trait_names.dedup();
362+
let n = trait_names.len();
363+
let stable = if all_stable { "" } else { "unstable " };
364+
let trait_ = if all_known { format!("trait{}", pluralize!(n)) } else { String::new() };
365+
format!("{stable}{trait_}{}", match &trait_names[..] {
366+
[t] => format!(" {t}"),
367+
[ts @ .., last] => format!(" {} and {last}", ts.join(", ")),
368+
[] => return false,
369+
},)
370+
} else {
371+
// We're more explicit when there's a mix of stable and unstable traits.
372+
let mut trait_names = constraints
373+
.iter()
374+
.map(|&(c, def_id, stable)| match def_id {
375+
None => format!("`{c}`"),
376+
Some(def_id) => format!("{stable}trait `{}`", tcx.item_name(def_id)),
377+
})
378+
.collect::<Vec<_>>();
379+
trait_names.sort();
380+
trait_names.dedup();
381+
match &trait_names[..] {
382+
[t] => t.to_string(),
383+
[ts @ .., last] => format!("{} and {last}", ts.join(", ")),
384+
[] => return false,
385+
}
386+
};
324387
let constraint = constraint.join(" + ");
325388
let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| {
326389
let suggestion = if span_to_replace.is_some() {
@@ -333,13 +396,11 @@ pub fn suggest_constraining_type_params<'a>(
333396
format!(" {constraint}")
334397
};
335398

336-
use SuggestChangingConstraintsMessage::RestrictBoundFurther;
337-
338399
if let Some(open_paren_sp) = open_paren_sp {
339-
suggestions.push((open_paren_sp, "(".to_string(), RestrictBoundFurther));
340-
suggestions.push((span, format!("){suggestion}"), RestrictBoundFurther));
400+
suggestions.push((open_paren_sp, post.clone(), "(".to_string(), bound_message));
401+
suggestions.push((span, post.clone(), format!("){suggestion}"), bound_message));
341402
} else {
342-
suggestions.push((span, suggestion, RestrictBoundFurther));
403+
suggestions.push((span, post.clone(), suggestion, bound_message));
343404
}
344405
};
345406

@@ -397,7 +458,8 @@ pub fn suggest_constraining_type_params<'a>(
397458
// - insert: `, X: Bar`
398459
suggestions.push((
399460
generics.tail_span_for_predicate_suggestion(),
400-
constraints.iter().fold(String::new(), |mut string, &(constraint, _)| {
461+
post,
462+
constraints.iter().fold(String::new(), |mut string, &(constraint, _, _)| {
401463
write!(string, ", {param_name}: {constraint}").unwrap();
402464
string
403465
}),
@@ -426,6 +488,7 @@ pub fn suggest_constraining_type_params<'a>(
426488
// default (`<T=Foo>`), so we suggest adding `where T: Bar`.
427489
suggestions.push((
428490
generics.tail_span_for_predicate_suggestion(),
491+
post,
429492
format!("{where_prefix} {param_name}: {constraint}"),
430493
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
431494
));
@@ -439,6 +502,7 @@ pub fn suggest_constraining_type_params<'a>(
439502
if let Some(colon_span) = param.colon_span {
440503
suggestions.push((
441504
colon_span.shrink_to_hi(),
505+
post,
442506
format!(" {constraint}"),
443507
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
444508
));
@@ -451,6 +515,7 @@ pub fn suggest_constraining_type_params<'a>(
451515
// - help: consider restricting this type parameter with `T: Foo`
452516
suggestions.push((
453517
param.span.shrink_to_hi(),
518+
post,
454519
format!(": {constraint}"),
455520
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
456521
));
@@ -459,39 +524,46 @@ pub fn suggest_constraining_type_params<'a>(
459524
// FIXME: remove the suggestions that are from derive, as the span is not correct
460525
suggestions = suggestions
461526
.into_iter()
462-
.filter(|(span, _, _)| !span.in_derive_expansion())
527+
.filter(|(span, _, _, _)| !span.in_derive_expansion())
463528
.collect::<Vec<_>>();
464-
529+
let suggested = !suggestions.is_empty();
465530
if suggestions.len() == 1 {
466-
let (span, suggestion, msg) = suggestions.pop().unwrap();
531+
let (span, post, suggestion, msg) = suggestions.pop().unwrap();
467532
let msg = match msg {
468533
SuggestChangingConstraintsMessage::RestrictBoundFurther => {
469-
Cow::from("consider further restricting this bound")
534+
format!("consider further restricting this bound")
535+
}
536+
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty }
537+
| SuggestChangingConstraintsMessage::RestrictType { ty }
538+
if ty.starts_with("impl ") =>
539+
{
540+
format!("consider restricting opaque type `{ty}` with {post}")
470541
}
471542
SuggestChangingConstraintsMessage::RestrictType { ty } => {
472-
Cow::from(format!("consider restricting type parameter `{ty}`"))
543+
format!("consider restricting type parameter `{ty}` with {post}")
473544
}
474545
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
475-
Cow::from(format!("consider further restricting type parameter `{ty}`"))
546+
format!("consider further restricting type parameter `{ty}` with {post}")
476547
}
477548
SuggestChangingConstraintsMessage::RemoveMaybeUnsized => {
478-
Cow::from("consider removing the `?Sized` bound to make the type parameter `Sized`")
549+
format!("consider removing the `?Sized` bound to make the type parameter `Sized`")
479550
}
480551
SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized => {
481-
Cow::from("consider replacing `?Sized` with `Sized`")
552+
format!("consider replacing `?Sized` with `Sized`")
482553
}
483554
};
484555

485556
err.span_suggestion_verbose(span, msg, suggestion, applicability);
486557
} else if suggestions.len() > 1 {
558+
let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" };
487559
err.multipart_suggestion_verbose(
488-
"consider restricting type parameters",
489-
suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(),
560+
format!("consider restricting type parameters{post}"),
561+
suggestions.into_iter().map(|(span, _, suggestion, _)| (span, suggestion)).collect(),
490562
applicability,
491563
);
492564
}
493565

494-
true
566+
suggested
495567
}
496568

497569
/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub fn baz<T>(t: std::ops::Range<T>) {
2+
for _ in t {}
3+
}
4+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0277]: the trait bound `T: Step` is not satisfied
2+
--> missing-bound.rs:2:14
3+
|
4+
2 | for _ in t {}
5+
| ^ the trait `Step` is not implemented for `T`
6+
|
7+
= note: required for `std::ops::Range<T>` to implement `Iterator`
8+
= note: required for `std::ops::Range<T>` to implement `IntoIterator`
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ only-linux
2+
//@ ignore-wasm32
3+
//@ ignore-wasm64
4+
// ignore-tidy-linelength
5+
6+
// Ensure that on stable we don't suggest restricting with an unsafe trait and we continue
7+
// mentioning the rest of the obligation chain.
8+
9+
use run_make_support::{diff, rust_lib_name, rustc};
10+
11+
fn main() {
12+
let out = rustc()
13+
.env("RUSTC_BOOTSTRAP", "-1")
14+
.input("missing-bound.rs")
15+
.run_fail()
16+
.assert_stderr_not_contains("help: consider restricting type parameter `T`")
17+
.assert_stderr_contains(
18+
r#"
19+
= note: required for `std::ops::Range<T>` to implement `Iterator`
20+
= note: required for `std::ops::Range<T>` to implement `IntoIterator`"#,
21+
)
22+
.stderr_utf8();
23+
diff().expected_file("missing-bound.stderr").actual_text("(stable rustc)", &out).run()
24+
}

tests/rustdoc-ui/issues/issue-96287.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0220]: associated type `Assoc` not found for `V`
44
LL | pub type Foo<V> = impl Trait<V::Assoc>;
55
| ^^^^^ there is an associated type `Assoc` in the trait `TraitWithAssoc`
66
|
7-
help: consider restricting type parameter `V`
7+
help: consider restricting type parameter `V` with trait `TraitWithAssoc`
88
|
99
LL | pub type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>;
1010
| ++++++++++++++++
@@ -16,7 +16,7 @@ LL | pub type Foo<V> = impl Trait<V::Assoc>;
1616
| ^^^^^ there is an associated type `Assoc` in the trait `TraitWithAssoc`
1717
|
1818
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
19-
help: consider restricting type parameter `V`
19+
help: consider restricting type parameter `V` with trait `TraitWithAssoc`
2020
|
2121
LL | pub type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>;
2222
| ++++++++++++++++

tests/rustdoc-ui/synthetic-auto-trait-impls/projections-in-super-trait-bound-unsatisfied.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0277]: the trait bound `C: Bar<5>` is not satisfied
44
LL | pub struct Structure<C: Tec> {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar<5>` is not implemented for `C`
66
|
7-
help: consider further restricting this bound
7+
help: consider further restricting type parameter `C` with trait `Bar`
88
|
99
LL | pub struct Structure<C: Tec + Bar<5>> {
1010
| ++++++++
@@ -15,7 +15,7 @@ error[E0277]: the trait bound `C: Bar<5>` is not satisfied
1515
LL | _field: C::BarType,
1616
| ^^^^^^^^^^ the trait `Bar<5>` is not implemented for `C`
1717
|
18-
help: consider further restricting this bound
18+
help: consider further restricting type parameter `C` with trait `Bar`
1919
|
2020
LL | pub struct Structure<C: Tec + Bar<5>> {
2121
| ++++++++

0 commit comments

Comments
 (0)