-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Explicit help message for binop type mismatch #40565
Conversation
r? @pnkfelix (rust_highfive has picked a reviewer for you, use r? to override) |
c8cea56
to
f8e0cc5
Compare
CCing the people involved in the referenced tickets: @beamspease, @abonander, @brson, @killercup, @oli-obk, @frewsxcv Could I get some feedback on the current output of this PR? |
I like it. What does it print for This isn't specific to this PR, I know, but I kind of wish that error messages didn't use full paths for types, because that's just noise. I would like to see error messages use type names based on how they're imported, but resolving that is nontrivial. |
@abonander, at the moment it's keeping the current output for |
op, | ||
trait_ref.substs()[1])); | ||
} else if &trait_def == "std::cmp::PartialEq" { | ||
err.help(&format!("can't compare `{}` against `{:?}`", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both could be Display format specifiers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kind
doesn't implement Display
and using Ty
instead would not work for cases with impl
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"with" instead of "against"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
// similar impls. | ||
let impl_candidates = self.find_similar_impl_candidates(trait_ref); | ||
self.report_similar_impl_candidates(impl_candidates, &mut err); | ||
let binops: FxHashMap<&str, &str> = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't work on no_std.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you clarify what wouldn't work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a crate with no_std, these path start with core
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added core::*
versions to the list. Feels hacky...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use lang items please - tcx.lang_items.add_trait
etc. - instead of guessing stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
<&'a u32 as std::ops::Add<u32>> | ||
<u32 as std::ops::Add<&'a u32>> | ||
<&'b u32 as std::ops::Add<&'a u32>> | ||
= help: `u32 + ()` has no implementation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The suggestions should still show up. Maybe even in the Format oft this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I felt that for these cases in particular, in order to keep it newbie friendly, it was better to simplify the text in order to make the error less daunting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case of &a + a
I think it would be very helpful to see the hints
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough. Should I only show them in the &a + a
case then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be enough for the cases I've run into with ppl I've taught rust
13 | 1 as usize - Some(1); | ||
| ^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Sub<std::option::Option<{integer}>>` is not implemented for `usize` | ||
| | ||
= help: `usize - std::option::Option<{integer}>` has no implementation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The note is already duplicating the main error message. You could remove the note and make your help a note
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense. Something along the lines of this?:
error[E0277]: the trait bound `usize: std::ops::Sub<std::option::Option<{integer}>>` is not satisfied
--> $DIR/binops.rs:13:5
|
13 | 1 as usize - Some(1);
| ^^^^^^^^^^^^^^^^^^^^ no implementation for `usize - std::option::Option<{integer}>`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Message like this can be introduced without changing the compiler by adding a #[on_unimplemented] attribute onto the traits. |
That wouldn't let me remove the "alternative implementations" recomendation or replace the span label. |
Very nice |
Ping. |
Oh I'm glad you're tackling this one! Can you add tests for a couple more operators? I like the way it looks, but just wanted to make sure other operators looked like it seems like they should. |
Awesome! :D |
What's the problem with using I don't see why the rendering here should be different then for, e.g., |
@arielb1, I really want to remove the suggestions because for these cases it is more confusing than helpful: error[E0277]: the trait bound `{integer}: std::ops::Add<std::option::Option<{integer}>>` is not satisfied
--> <anon>:3:5
|
3 | 1 + Some(1);
| ^^^^^^^^^^^ the trait `std::ops::Add<std::option::Option<{integer}>>` is not implemented for `{integer}`
|
= help: the following implementations were found:
<std::borrow::Cow<'a, str> as std::ops::Add<&'a str>>
<std::borrow::Cow<'a, str> as std::ops::Add>
<u32 as std::ops::Add>
<&'a u32 as std::ops::Add<u32>>
and 106 others
error[E0277]: the trait bound `usize: std::ops::Sub<std::option::Option<{integer}>>` is not satisfied
--> <anon>:4:5
|
4 | 1 as usize - Some(1);
| ^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Sub<std::option::Option<{integer}>>` is not implemented for `usize`
|
= help: the following implementations were found:
<usize as std::ops::Sub>
<&'a usize as std::ops::Sub<usize>>
<usize as std::ops::Sub<&'a usize>>
<&'b usize as std::ops::Sub<&'a usize>>
error[E0277]: the trait bound `{integer}: std::cmp::PartialEq<std::result::Result<{integer}, _>>` is not satisfied
--> <anon>:5:5
|
5 | 1 == Ok(1);
| ^^^^^^^^^^ the trait `std::cmp::PartialEq<std::result::Result<{integer}, _>>` is not implemented for `{integer}`
|
= help: the following implementations were found:
<&'a A as std::cmp::PartialEq<&'b B>>
<&'a mut A as std::cmp::PartialEq<&'b mut B>>
<&'a A as std::cmp::PartialEq<&'b mut B>>
<&'a mut A as std::cmp::PartialEq<&'b B>>
and 697 others vs error[E0277]: the trait bound `{integer}: std::ops::Add<std::option::Option<{integer}>>` is not satisfied
--> $DIR/binops.rs:12:5
|
12 | 1 + Some(1);
| ^^^^^^^^^^^ no implementation for `{integer} + std::option::Option<{integer}>`
error[E0277]: the trait bound `usize: std::ops::Sub<std::option::Option<{integer}>>` is not satisfied
--> $DIR/binops.rs:13:5
|
13 | 1 as usize - Some(1);
| ^^^^^^^^^^^^^^^^^^^^ no implementation for `usize - std::option::Option<{integer}>`
error[E0277]: the trait bound `{integer}: std::cmp::PartialEq<std::result::Result<{integer}, _>>` is not satisfied
--> $DIR/binops.rs:14:5
|
14 | 1 == Ok(1);
| ^^^^^^^^^^ can't compare `{integer}` with `std::result::Result<{integer}, _>` Using error[E0277]: the trait bound `{integer}: std::ops::Add<std::option::Option<{integer}>>` is not satisfied
--> <anon>:3:5
|
3 | 1 + Some(1);
| ^^^^^^^^^^^ the trait `std::ops::Add<std::option::Option<{integer}>>` is not implemented for `{integer}`
|
= help: the following implementations were found:
<std::borrow::Cow<'a, str> as std::ops::Add<&'a str>>
<std::borrow::Cow<'a, str> as std::ops::Add>
<u32 as std::ops::Add>
<&'a u32 as std::ops::Add<u32>>
and 106 others
= note: no implementation for `{integer} + std::option::Option<{integer}>`
error[E0277]: the trait bound `usize: std::ops::Sub<std::option::Option<{integer}>>` is not satisfied
--> <anon>:4:5
|
4 | 1 as usize - Some(1);
| ^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Sub<std::option::Option<{integer}>>` is not implemented for `usize`
|
= help: the following implementations were found:
<usize as std::ops::Sub>
<&'a usize as std::ops::Sub<usize>>
<usize as std::ops::Sub<&'a usize>>
<&'b usize as std::ops::Sub<&'a usize>>
= note: no implementation for `usize + std::option::Option<{integer}>`
error[E0277]: the trait bound `{integer}: std::cmp::PartialEq<std::result::Result<{integer}, _>>` is not satisfied
--> <anon>:5:5
|
5 | 1 == Ok(1);
| ^^^^^^^^^^ the trait `std::cmp::PartialEq<std::result::Result<{integer}, _>>` is not implemented for `{integer}`
|
= help: the following implementations were found:
<&'a A as std::cmp::PartialEq<&'b B>>
<&'a mut A as std::cmp::PartialEq<&'b mut B>>
<&'a A as std::cmp::PartialEq<&'b mut B>>
<&'a mut A as std::cmp::PartialEq<&'b B>>
and 697 others
= note: no implementation for `{integer} + std::cmp::Result<{integer}, _>` which I feel is not approachable enough. |
But if you use |
You're right, of course. I don't think there's a way to get the types involved on |
What's the problem with adding something like: #[rustc_on_unimplemented = "no implementation for `{Self} + {RHS}`"] If there is something missing, can it be added to |
@arielb1 I'm so sorry, I only now see what you mean. I had completely missed that this attribute takes arguments. Then, the difference between this PR and your suggestion is much smaller than I thought: error[E0277]: the trait bound `{integer}: std::ops::Add<std::option::Option<{integer}>>` is not satisfied
--> $DIR/binops.rs:12:5
|
12 | 1 + Some(1);
| ^^^^^^^^^^^ no implementation for `{integer} + std::option::Option<{integer}>`
error[E0277]: the trait bound `usize: std::ops::Sub<std::option::Option<{integer}>>` is not satisfied
--> $DIR/binops.rs:13:5
|
13 | 1 as usize - Some(1);
| ^^^^^^^^^^^^^^^^^^^^ no implementation for `usize - std::option::Option<{integer}>`
error[E0277]: the trait bound `{integer}: std::cmp::PartialEq<std::result::Result<{integer}, _>>` is not satisfied
--> $DIR/binops.rs:14:5
|
14 | 1 == Ok(1);
| ^^^^^^^^^^ can't compare `{integer}` with `std::result::Result<{integer}, _>` vs error[E0277]: the trait bound `{integer}: std::ops::Add<std::option::Option<{integer}>>` is not satisfied
--> <anon>:3:5
|
3 | 1 + Some(1);
| ^^^^^^^^^^^ the trait `std::ops::Add<std::option::Option<{integer}>>` is not implemented for `{integer}`
|
= note: no implementation for `{integer} + std::option::Option<{integer}>`
error[E0277]: the trait bound `usize: std::ops::Sub<std::option::Option<{integer}>>` is not satisfied
--> <anon>:4:5
|
4 | 1 as usize - Some(1);
| ^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::Sub<std::option::Option<{integer}>>` is not implemented for `usize`
|
= note: no implementation for `usize + std::option::Option<{integer}>`
error[E0277]: the trait bound `{integer}: std::cmp::PartialEq<std::result::Result<{integer}, _>>` is not satisfied
--> <anon>:5:5
|
5 | 1 == Ok(1);
| ^^^^^^^^^^ the trait `std::cmp::PartialEq<std::result::Result<{integer}, _>>` is not implemented for `{integer}`
|
= note: can't compare `{integer}` with `std::result::Result<{integer}, _>` I still believe the first version is nicer for newcomers, but the second option is already a huge improvement over the status quo. I'll be updating this PR to use |
4aab465
to
23af7b4
Compare
@@ -487,15 +487,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |||
obligation: &PredicateObligation<'tcx>, | |||
error: &SelectionError<'tcx>) | |||
{ | |||
let span = obligation.cause.span; | |||
let sp = obligation.cause.span; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you change this back to span
and squash?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@@ -544,25 +537,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |||
// which is somewhat confusing. | |||
err.help(&format!("consider adding a `where {}` bound", | |||
trait_ref.to_predicate())); | |||
} else if let Some(s) = self.on_unimplemented_note(trait_ref, | |||
obligation) { | |||
} else if let Some(s) = self.on_unimplemented_note(trait_ref, obligation) { | |||
// If it has a custom "#[rustc_on_unimplemented]" | |||
// error message, let's display it! | |||
err.note(&s); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can change all on_unimplemented
messages to be labels by making this a .span_label
call. Not sure it's a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a bad idea to do that for all cases, but I've been considering the possibility of having a way to configure wether it is shown as a note or as the span's label. I'm thinking that it could either accomplished by changing the parsing of attributes to accept extra arguments, or simply have it change behavior when the rustc_on_unimplemented
text starts with label:
. It might be too more magic than I'm comfortable with, though.
r+ modulo squash. |
📌 Commit 9aa75a8 has been approved by |
@bors r- |
@bors r+ |
📌 Commit 9aa75a8 has been approved by |
When trying to do a binary operation with missing implementation, for example `1 + Some(2)`, provide an explicit help message: ``` note: no implementation for `{integer} + std::option::Option<{integer}>` ``` Use `rustc_on_unimplemented` for the suggestions. Move cfail test to ui.
@bors r=arielb1 hadn't seen the comment about |
📌 Commit be8787d has been approved by |
Explicit help message for binop type mismatch When trying to do `1 + Some(2)`, or some other binary operation on two types different types without an appropriate trait implementation, provide an explicit help message: ```rust help: `{integer} + std::option::Option<{integer}>` has no implementation ``` Re: #39579, #38564, #37626, #39942, #34698.
☀️ Test successful - status-appveyor, status-travis |
When trying to do
1 + Some(2)
, or some other binary operation on twotypes different types without an appropriate trait implementation, provide
an explicit help message:
Re: #39579, #38564, #37626, #39942, #34698.