Skip to content

Commit

Permalink
Suggest creating unary tuples
Browse files Browse the repository at this point in the history
  • Loading branch information
mejrs committed Nov 4, 2024
1 parent 1e4f10b commit 54ffd28
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}

self.try_to_add_help_message(
&root_obligation,
&obligation,
leaf_trait_predicate,
&mut err,
Expand Down Expand Up @@ -2428,6 +2429,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {

fn try_to_add_help_message(
&self,
root_obligation: &PredicateObligation<'tcx>,
obligation: &PredicateObligation<'tcx>,
trait_predicate: ty::PolyTraitPredicate<'tcx>,
err: &mut Diag<'_>,
Expand Down Expand Up @@ -2511,6 +2513,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
impl_candidates.as_slice(),
span,
);

self.suggest_tuple_wrapping(err, root_obligation, obligation, span);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4436,6 +4436,43 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}

/// If the type failed selection but the trait is implemented for `(T,)`, suggest that the user
/// creates a unary tuple
///
/// This is a common gotcha when using libraries that emulate variadic functions with traits for tuples.
pub(super) fn suggest_tuple_wrapping(
&self,
err: &mut Diag<'_>,
root_obligation: &PredicateObligation<'tcx>,
obligation: &PredicateObligation<'tcx>,
span: Span,
) {
if !matches!(obligation.cause.code(), ObligationCauseCode::FunctionArg { .. }) {
return;
}

let Some(root_pred) = root_obligation.predicate.as_trait_clause() else { return };

let trait_ref = root_pred.map_bound(|root_pred| {
root_pred
.trait_ref
.with_self_ty(self.tcx, Ty::new_tup(self.tcx, &[root_pred.trait_ref.self_ty()]))
});

self.infcx.enter_forall(trait_ref, |trait_ref| {
if self
.type_implements_trait(trait_ref.def_id, trait_ref.args, root_obligation.param_env)
.must_apply_modulo_regions()
{
err.multipart_suggestion_verbose(
format!("use a unary tuple instead"),
vec![(span.shrink_to_lo(), "(".into()), (span.shrink_to_hi(), ",)".into())],
Applicability::MaybeIncorrect,
);
}
});
}

pub(super) fn explain_hrtb_projection(
&self,
diag: &mut Diag<'_>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ note: required by a bound in `check`
|
LL | fn check(a: impl Foo) {}
| ^^^ required by this bound in `check`
help: use a unary tuple instead
|
LL | check(((),));
| + ++

error: aborting due to 1 previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ note: required by a bound in `check`
|
LL | fn check(a: impl Foo) {}
| ^^^ required by this bound in `check`
help: use a unary tuple instead
|
LL | check(((),));
| + ++

error: aborting due to 1 previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ note: required by a bound in `check`
|
LL | fn check(a: impl Foo) {}
| ^^^ required by this bound in `check`
help: use a unary tuple instead
|
LL | check(((),));
| + ++

error: aborting due to 1 previous error

Expand Down
4 changes: 4 additions & 0 deletions tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ LL | cachedcoso.call_once(1);
|
note: required by a bound in `call_once`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
help: use a unary tuple instead
|
LL | cachedcoso.call_once((1,));
| + ++

error: aborting due to 6 previous errors

Expand Down
19 changes: 19 additions & 0 deletions tests/ui/on-unimplemented/suggest_tuple_wrap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pub trait Argument {}
impl Argument for u8 {}
impl Argument for i8 {}
impl Argument for String {}
impl Argument for &str {}

pub trait TupleArgs {}
impl<A: Argument> TupleArgs for (A,) {}
impl<A: Argument, B: Argument> TupleArgs for (A, B) {}
impl<A: Argument, B: Argument, C: Argument> TupleArgs for (A, B, C) {}

fn convert_into_tuple(_x: impl TupleArgs) {}

fn main() {
convert_into_tuple(42_u8);
//~^ ERROR E0277
//~| HELP the following other types implement trait `TupleArgs`
//~| HELP use a unary tuple instead
}
25 changes: 25 additions & 0 deletions tests/ui/on-unimplemented/suggest_tuple_wrap.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
error[E0277]: the trait bound `u8: TupleArgs` is not satisfied
--> $DIR/suggest_tuple_wrap.rs:15:24
|
LL | convert_into_tuple(42_u8);
| ------------------ ^^^^^ the trait `TupleArgs` is not implemented for `u8`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `TupleArgs`:
(A, B)
(A, B, C)
(A,)
note: required by a bound in `convert_into_tuple`
--> $DIR/suggest_tuple_wrap.rs:12:32
|
LL | fn convert_into_tuple(_x: impl TupleArgs) {}
| ^^^^^^^^^ required by this bound in `convert_into_tuple`
help: use a unary tuple instead
|
LL | convert_into_tuple((42_u8,));
| + ++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
26 changes: 26 additions & 0 deletions tests/ui/on-unimplemented/suggest_tuple_wrap_root_obligation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
struct Tuple;

impl From<(u8,)> for Tuple {
fn from(_: (u8,)) -> Self {
todo!()
}
}
impl From<(u8, u8)> for Tuple {
fn from(_: (u8, u8)) -> Self {
todo!()
}
}
impl From<(u8, u8, u8)> for Tuple {
fn from(_: (u8, u8, u8)) -> Self {
todo!()
}
}

fn convert_into_tuple(_x: impl Into<Tuple>) {}

fn main() {
convert_into_tuple(42_u8);
//~^ ERROR E0277
//~| HELP use a unary tuple instead
//~| HELP the following other types implement trait `From<T>`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
error[E0277]: the trait bound `Tuple: From<u8>` is not satisfied
--> $DIR/suggest_tuple_wrap_root_obligation.rs:22:24
|
LL | convert_into_tuple(42_u8);
| ------------------ ^^^^^ the trait `From<u8>` is not implemented for `Tuple`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `From<T>`:
`Tuple` implements `From<(u8, u8)>`
`Tuple` implements `From<(u8, u8, u8)>`
`Tuple` implements `From<(u8,)>`
= note: required for `u8` to implement `Into<Tuple>`
note: required by a bound in `convert_into_tuple`
--> $DIR/suggest_tuple_wrap_root_obligation.rs:19:32
|
LL | fn convert_into_tuple(_x: impl Into<Tuple>) {}
| ^^^^^^^^^^^ required by this bound in `convert_into_tuple`
help: use a unary tuple instead
|
LL | convert_into_tuple((42_u8,));
| + ++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
4 changes: 4 additions & 0 deletions tests/ui/overloaded/overloaded-calls-nontuple.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ LL | self.call_mut(z)
|
note: required by a bound in `call_mut`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
help: use a unary tuple instead
|
LL | self.call_mut((z,))
| + ++

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

0 comments on commit 54ffd28

Please sign in to comment.