Skip to content

Commit 5a48fe2

Browse files
committed
Suggest creating unary tuples
1 parent 1e4f10b commit 5a48fe2

11 files changed

+155
-0
lines changed

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

+4
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
466466
}
467467

468468
self.try_to_add_help_message(
469+
&root_obligation,
469470
&obligation,
470471
leaf_trait_predicate,
471472
&mut err,
@@ -2428,6 +2429,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
24282429

24292430
fn try_to_add_help_message(
24302431
&self,
2432+
root_obligation: &PredicateObligation<'tcx>,
24312433
obligation: &PredicateObligation<'tcx>,
24322434
trait_predicate: ty::PolyTraitPredicate<'tcx>,
24332435
err: &mut Diag<'_>,
@@ -2511,6 +2513,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
25112513
impl_candidates.as_slice(),
25122514
span,
25132515
);
2516+
2517+
self.suggest_tuple_wrapping(err, root_obligation, obligation);
25142518
}
25152519
}
25162520

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

+35
Original file line numberDiff line numberDiff line change
@@ -4436,6 +4436,41 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
44364436
}
44374437
}
44384438

4439+
/// If the type failed selection but the trait is implemented for `(T,)`, suggest that the user
4440+
/// creates a unary tuple
4441+
///
4442+
/// This is a common gotcha when using libraries that emulate variadic functions with traits for tuples.
4443+
pub(super) fn suggest_tuple_wrapping(
4444+
&self,
4445+
err: &mut Diag<'_>,
4446+
root_obligation: &PredicateObligation<'tcx>,
4447+
obligation: &PredicateObligation<'tcx>,
4448+
) {
4449+
let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code() else {
4450+
return;
4451+
};
4452+
4453+
let Some(root_pred) = root_obligation.predicate.as_trait_clause() else { return };
4454+
4455+
let trait_ref = root_pred.map_bound(|root_pred| {
4456+
root_pred
4457+
.trait_ref
4458+
.with_self_ty(self.tcx, Ty::new_tup(self.tcx, &[root_pred.trait_ref.self_ty()]))
4459+
});
4460+
4461+
let obligation =
4462+
Obligation::new(self.tcx, obligation.cause.clone(), obligation.param_env, trait_ref);
4463+
4464+
if self.predicate_must_hold_modulo_regions(&obligation) {
4465+
let arg_span = self.tcx.hir().span(*arg_hir_id);
4466+
err.multipart_suggestion_verbose(
4467+
format!("use a unary tuple instead"),
4468+
vec![(arg_span.shrink_to_lo(), "(".into()), (arg_span.shrink_to_hi(), ",)".into())],
4469+
Applicability::MaybeIncorrect,
4470+
);
4471+
}
4472+
}
4473+
44394474
pub(super) fn explain_hrtb_projection(
44404475
&self,
44414476
diag: &mut Diag<'_>,

tests/ui/diagnostic_namespace/do_not_recommend/do_not_apply_attribute_without_feature_flag.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ note: required by a bound in `check`
1515
|
1616
LL | fn check(a: impl Foo) {}
1717
| ^^^ required by this bound in `check`
18+
help: use a unary tuple instead
19+
|
20+
LL | check(((),));
21+
| + ++
1822

1923
error: aborting due to 1 previous error
2024

tests/ui/diagnostic_namespace/do_not_recommend/supress_suggestions_in_help.current.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ note: required by a bound in `check`
1212
|
1313
LL | fn check(a: impl Foo) {}
1414
| ^^^ required by this bound in `check`
15+
help: use a unary tuple instead
16+
|
17+
LL | check(((),));
18+
| + ++
1519

1620
error: aborting due to 1 previous error
1721

tests/ui/diagnostic_namespace/do_not_recommend/supress_suggestions_in_help.next.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ note: required by a bound in `check`
1212
|
1313
LL | fn check(a: impl Foo) {}
1414
| ^^^ required by this bound in `check`
15+
help: use a unary tuple instead
16+
|
17+
LL | check(((),));
18+
| + ++
1519

1620
error: aborting due to 1 previous error
1721

tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ LL | cachedcoso.call_once(1);
7171
|
7272
note: required by a bound in `call_once`
7373
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
74+
help: use a unary tuple instead
75+
|
76+
LL | cachedcoso.call_once((1,));
77+
| + ++
7478

7579
error: aborting due to 6 previous errors
7680

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
pub trait Argument {}
2+
impl Argument for u8 {}
3+
impl Argument for i8 {}
4+
impl Argument for String {}
5+
impl Argument for &str {}
6+
7+
pub trait TupleArgs {}
8+
impl<A: Argument> TupleArgs for (A,) {}
9+
impl<A: Argument, B: Argument> TupleArgs for (A, B) {}
10+
impl<A: Argument, B: Argument, C: Argument> TupleArgs for (A, B, C) {}
11+
12+
fn convert_into_tuple(_x: impl TupleArgs) {}
13+
14+
fn main() {
15+
convert_into_tuple(42_u8);
16+
//~^ ERROR E0277
17+
//~| HELP the following other types implement trait `TupleArgs`
18+
//~| HELP use a unary tuple instead
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0277]: the trait bound `u8: TupleArgs` is not satisfied
2+
--> $DIR/suggest_tuple_wrap.rs:15:24
3+
|
4+
LL | convert_into_tuple(42_u8);
5+
| ------------------ ^^^^^ the trait `TupleArgs` is not implemented for `u8`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the following other types implement trait `TupleArgs`:
10+
(A, B)
11+
(A, B, C)
12+
(A,)
13+
note: required by a bound in `convert_into_tuple`
14+
--> $DIR/suggest_tuple_wrap.rs:12:32
15+
|
16+
LL | fn convert_into_tuple(_x: impl TupleArgs) {}
17+
| ^^^^^^^^^ required by this bound in `convert_into_tuple`
18+
help: use a unary tuple instead
19+
|
20+
LL | convert_into_tuple((42_u8,));
21+
| + ++
22+
23+
error: aborting due to 1 previous error
24+
25+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
struct Tuple;
2+
3+
impl From<(u8,)> for Tuple {
4+
fn from(_: (u8,)) -> Self {
5+
todo!()
6+
}
7+
}
8+
impl From<(u8, u8)> for Tuple {
9+
fn from(_: (u8, u8)) -> Self {
10+
todo!()
11+
}
12+
}
13+
impl From<(u8, u8, u8)> for Tuple {
14+
fn from(_: (u8, u8, u8)) -> Self {
15+
todo!()
16+
}
17+
}
18+
19+
fn convert_into_tuple(_x: impl Into<Tuple>) {}
20+
21+
fn main() {
22+
convert_into_tuple(42_u8);
23+
//~^ ERROR E0277
24+
//~| HELP use a unary tuple instead
25+
//~| HELP the following other types implement trait `From<T>`
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0277]: the trait bound `Tuple: From<u8>` is not satisfied
2+
--> $DIR/suggest_tuple_wrap_root_obligation.rs:22:24
3+
|
4+
LL | convert_into_tuple(42_u8);
5+
| ------------------ ^^^^^ the trait `From<u8>` is not implemented for `Tuple`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the following other types implement trait `From<T>`:
10+
`Tuple` implements `From<(u8, u8)>`
11+
`Tuple` implements `From<(u8, u8, u8)>`
12+
`Tuple` implements `From<(u8,)>`
13+
= note: required for `u8` to implement `Into<Tuple>`
14+
note: required by a bound in `convert_into_tuple`
15+
--> $DIR/suggest_tuple_wrap_root_obligation.rs:19:32
16+
|
17+
LL | fn convert_into_tuple(_x: impl Into<Tuple>) {}
18+
| ^^^^^^^^^^^ required by this bound in `convert_into_tuple`
19+
help: use a unary tuple instead
20+
|
21+
LL | convert_into_tuple((42_u8,));
22+
| + ++
23+
24+
error: aborting due to 1 previous error
25+
26+
For more information about this error, try `rustc --explain E0277`.

tests/ui/overloaded/overloaded-calls-nontuple.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ LL | self.call_mut(z)
3838
|
3939
note: required by a bound in `call_mut`
4040
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
41+
help: use a unary tuple instead
42+
|
43+
LL | self.call_mut((z,))
44+
| + ++
4145

4246
error[E0059]: cannot use call notation; the first type parameter for the function trait is neither a tuple nor unit
4347
--> $DIR/overloaded-calls-nontuple.rs:29:10

0 commit comments

Comments
 (0)