Skip to content

Commit 32c97da

Browse files
committed
In some limited cases, suggest where bounds for non-type params
Partially address #81971.
1 parent 8fe989d commit 32c97da

File tree

7 files changed

+74
-4
lines changed

7 files changed

+74
-4
lines changed

compiler/rustc_middle/src/ty/diagnostics.rs

+30
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,36 @@ impl<'tcx> TyS<'tcx> {
7575
}
7676
}
7777

78+
pub fn suggest_arbitrary_trait_bound(
79+
generics: &hir::Generics<'_>,
80+
err: &mut DiagnosticBuilder<'_>,
81+
param_name: &str,
82+
constraint: &str,
83+
) -> bool {
84+
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
85+
match (param, param_name) {
86+
(Some(_), "Self") => return false,
87+
_ => {}
88+
}
89+
// Suggest a where clause bound for a non-type paremeter.
90+
let (action, prefix) = if generics.where_clause.predicates.is_empty() {
91+
("introducing a", " where ")
92+
} else {
93+
("extending the", ", ")
94+
};
95+
err.span_suggestion_verbose(
96+
generics.where_clause.tail_span_for_suggestion(),
97+
&format!(
98+
"consider {} `where` bound, but there might be an alternative better way to express \
99+
this requirement",
100+
action,
101+
),
102+
format!("{}{}: {}", prefix, param_name, constraint),
103+
Applicability::MaybeIncorrect,
104+
);
105+
true
106+
}
107+
78108
/// Suggest restricting a type param with a new bound.
79109
pub fn suggest_constraining_type_param(
80110
tcx: TyCtxt<'_>,

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+23-3
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use rustc_hir::intravisit::Visitor;
1717
use rustc_hir::lang_items::LangItem;
1818
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
1919
use rustc_middle::ty::{
20-
self, suggest_constraining_type_param, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty,
21-
TyCtxt, TypeFoldable, WithConstness,
20+
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
21+
Infer, InferTy, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
2222
};
2323
use rustc_middle::ty::{TypeAndMut, TypeckResults};
2424
use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -334,7 +334,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
334334
let (param_ty, projection) = match self_ty.kind() {
335335
ty::Param(_) => (true, None),
336336
ty::Projection(projection) => (false, Some(projection)),
337-
_ => return,
337+
_ => (false, None),
338338
};
339339

340340
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
@@ -453,6 +453,26 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
453453
}
454454
}
455455

456+
hir::Node::Item(hir::Item {
457+
kind:
458+
hir::ItemKind::Struct(_, generics)
459+
| hir::ItemKind::Enum(_, generics)
460+
| hir::ItemKind::Union(_, generics)
461+
| hir::ItemKind::Trait(_, _, generics, ..)
462+
| hir::ItemKind::Impl(hir::Impl { generics, .. })
463+
| hir::ItemKind::Fn(_, generics, _)
464+
| hir::ItemKind::TyAlias(_, generics)
465+
| hir::ItemKind::TraitAlias(generics, _)
466+
| hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
467+
..
468+
}) if !param_ty => {
469+
// Missing generic type parameter bound.
470+
let param_name = self_ty.to_string();
471+
let constraint = trait_ref.print_only_trait_path().to_string();
472+
if suggest_arbitrary_trait_bound(generics, &mut err, &param_name, &constraint) {
473+
return;
474+
}
475+
}
456476
hir::Node::Crate(..) => return,
457477

458478
_ => {}

src/test/ui/partialeq_help.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ LL | a == b;
55
| ^^ no implementation for `&T == T`
66
|
77
= help: the trait `PartialEq<T>` is not implemented for `&T`
8+
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
9+
|
10+
LL | fn foo<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
11+
| ^^^^^^^^^^^^^^^^^^^^^^
812

913
error: aborting due to previous error
1014

src/test/ui/specialization/deafult-associated-type-bound-2.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ LL | default type U = &'static B;
1818
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `&'static B == B`
1919
|
2020
= help: the trait `PartialEq<B>` is not implemented for `&'static B`
21+
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
22+
|
23+
LL | impl<B: 'static, T> X<B> for T where &'static B: PartialEq<B> {
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2125

2226
error: aborting due to previous error; 1 warning emitted
2327

src/test/ui/suggestions/suggest-change-mut.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use std::io::{BufRead, BufReader, Read, Write};
44

5-
fn issue_81421<T: Read + Write>(mut stream: T) {
5+
fn issue_81421<T: Read + Write>(mut stream: T) { //~ HELP consider introducing a `where` bound
66
let initial_message = format!("Hello world");
77
let mut buffer: Vec<u8> = Vec::new();
88
let bytes_written = stream.write_all(initial_message.as_bytes());

src/test/ui/suggestions/suggest-change-mut.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ help: consider removing the leading `&`-reference
99
|
1010
LL | let mut stream_reader = BufReader::new(stream);
1111
| --
12+
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
13+
|
14+
LL | fn issue_81421<T: Read + Write>(mut stream: T) where &T: std::io::Read {
15+
| ^^^^^^^^^^^^^^^^^^^^^^^
1216
help: consider changing this borrow's mutability
1317
|
1418
LL | let mut stream_reader = BufReader::new(&mut stream);

src/test/ui/traits/suggest-where-clause.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ LL | <u64 as From<T>>::from;
3535
| ^^^^^^^^^^^^^^^^^^^^^^ the trait `From<T>` is not implemented for `u64`
3636
|
3737
= note: required by `from`
38+
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
39+
|
40+
LL | fn check<T: Iterator, U: ?Sized>() where u64: From<T> {
41+
| ^^^^^^^^^^^^^^^^^^
3842

3943
error[E0277]: the trait bound `u64: From<<T as Iterator>::Item>` is not satisfied
4044
--> $DIR/suggest-where-clause.rs:18:5
@@ -43,6 +47,10 @@ LL | <u64 as From<<T as Iterator>::Item>>::from;
4347
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<<T as Iterator>::Item>` is not implemented for `u64`
4448
|
4549
= note: required by `from`
50+
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
51+
|
52+
LL | fn check<T: Iterator, U: ?Sized>() where u64: From<<T as Iterator>::Item> {
53+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4654

4755
error[E0277]: the trait bound `Misc<_>: From<T>` is not satisfied
4856
--> $DIR/suggest-where-clause.rs:23:5

0 commit comments

Comments
 (0)