Skip to content
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

Merged
merged 1 commit into from
Apr 11, 2017

Conversation

estebank
Copy link
Contributor

@estebank estebank commented Mar 16, 2017

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:

help: `{integer} + std::option::Option<{integer}>` has no implementation

Re: #39579, #38564, #37626, #39942, #34698.

@rust-highfive
Copy link
Collaborator

r? @pnkfelix

(rust_highfive has picked a reviewer for you, use r? to override)

@estebank estebank force-pushed the binops-help branch 3 times, most recently from c8cea56 to f8e0cc5 Compare March 16, 2017 04:18
@estebank
Copy link
Contributor Author

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?

@abonander
Copy link
Contributor

abonander commented Mar 16, 2017

I like it. What does it print for PartialOrd operators? Same as for PartialEq?

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.

@estebank
Copy link
Contributor Author

What does it print for PartialOrd operators?

@abonander, at the moment it's keeping the current output for PartialOrd, << and >>.

op,
trait_ref.substs()[1]));
} else if &trait_def == "std::cmp::PartialEq" {
err.help(&format!("can't compare `{}` against `{:?}`",
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"with" instead of "against"

Copy link
Contributor Author

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> = [
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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

Copy link
Contributor Author

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...

Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor Author

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?

Copy link
Contributor

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
Copy link
Contributor

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

Copy link
Contributor Author

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}>`

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@nagisa
Copy link
Member

nagisa commented Mar 16, 2017

Message like this can be introduced without changing the compiler by adding a #[on_unimplemented] attribute onto the traits.

@estebank
Copy link
Contributor Author

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.

@estebank estebank changed the title Explicit help message for binop type missmatch Explicit help message for binop type mismatch Mar 21, 2017
@estebank
Copy link
Contributor Author

Current output:

@beamspease
Copy link

Very nice

@estebank
Copy link
Contributor Author

estebank commented Apr 3, 2017

@sophiajt
Copy link
Contributor

sophiajt commented Apr 3, 2017

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.

@GuillaumeGomez
Copy link
Member

Awesome! :D

@arielb1
Copy link
Contributor

arielb1 commented Apr 4, 2017

@estebank

What's the problem with using #[rustc_on_unimplemented] again? That it does not affect the primary label? Maybe rustc_on_unimplemented should be a label instead of being a note?

I don't see why the rendering here should be different then for, e.g., Sized.

@estebank
Copy link
Contributor Author

estebank commented Apr 4, 2017

@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 rustc_on_unimplemented the output would look this way:

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.

@arielb1
Copy link
Contributor

arielb1 commented Apr 6, 2017

@estebank

But if you use rustc_on_unimplemented, the "following implementations were found" message is suppressed.

@estebank
Copy link
Contributor Author

estebank commented Apr 7, 2017

You're right, of course. I don't think there's a way to get the types involved on rustc_on_unimplemented, though, to accomplish the same kind of message proposed on this PR.

@arielb1
Copy link
Contributor

arielb1 commented Apr 8, 2017

@estebank

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 rustc_on_unimplemented? It might be useful to other people too.

@estebank
Copy link
Contributor Author

estebank commented Apr 9, 2017

@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 rustc_on_unimplemented instead. Sorry for the misunderstanding on my part.

@estebank estebank force-pushed the binops-help branch 2 times, most recently from 4aab465 to 23af7b4 Compare April 10, 2017 10:02
@@ -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;
Copy link
Contributor

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?

Copy link
Contributor Author

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);
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@arielb1
Copy link
Contributor

arielb1 commented Apr 10, 2017

r+ modulo squash.

@estebank
Copy link
Contributor Author

@bors r=arielb1

@arielb1 squashed into single commit.

@bors
Copy link
Contributor

bors commented Apr 10, 2017

📌 Commit 9aa75a8 has been approved by arielb1

@estebank
Copy link
Contributor Author

@bors r-

@arielb1
Copy link
Contributor

arielb1 commented Apr 10, 2017

@bors r+

@bors
Copy link
Contributor

bors commented Apr 10, 2017

📌 Commit 9aa75a8 has been approved by arielb1

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.
@estebank
Copy link
Contributor Author

@bors r=arielb1

hadn't seen the comment about sp -> span, changed.

@bors
Copy link
Contributor

bors commented Apr 10, 2017

📌 Commit be8787d has been approved by arielb1

@bors
Copy link
Contributor

bors commented Apr 10, 2017

⌛ Testing commit be8787d with merge 730e5ad...

bors added a commit that referenced this pull request Apr 10, 2017
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.
@bors
Copy link
Contributor

bors commented Apr 11, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: arielb1
Pushing 730e5ad to master...

@bors bors merged commit be8787d into rust-lang:master Apr 11, 2017
@estebank estebank deleted the binops-help branch November 9, 2023 05:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.