Skip to content

Add deref suggestion #43870

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

Merged
merged 2 commits into from
Sep 23, 2017
Merged

Add deref suggestion #43870

merged 2 commits into from
Sep 23, 2017

Conversation

GuillaumeGomez
Copy link
Member

Fixes #34562.

@rust-highfive
Copy link
Contributor

r? @pnkfelix

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

|
= note: expected type `std::string::String`
found type `&std::string::String`
= help: try with `*&"aaa".to_owned()`
Copy link
Contributor

Choose a reason for hiding this comment

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

can't it check if & is already there, or is that too difficult?

Copy link
Member

@kennytm kennytm Aug 15, 2017

Choose a reason for hiding this comment

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

Worse, the suggestion isn't even valid.

   Compiling playground v0.0.1 (file:///playground)
error[E0507]: cannot move out of borrowed content
 --> src/main.rs:4:9
  |
4 |     foo(*&"aaa".to_owned());
  |         ^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content

error: aborting due to previous error(s)

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

Copy link
Member Author

Choose a reason for hiding this comment

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

@durka: It's pretty simple so I'll do it.
@kennytm: Hum... I guess I'll need to add a check over the dereference...

return if src.starts_with("&") {
Some(format!("try with `{}`", &src[1..]))
} else {
Some(format!("try with `*{}`", &src))
Copy link
Member Author

@GuillaumeGomez GuillaumeGomez Aug 15, 2017

Choose a reason for hiding this comment

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

For this case, I'd need to check if it's actually possible to deref without error but I don't know how to do it. Anyone has an idea?

Copy link
Member

@kennytm kennytm Aug 15, 2017

Choose a reason for hiding this comment

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

@GuillaumeGomez You could check if the target type implements Copy I guess.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's only one possibility. In the ui test I added, it's not necessary (and the String type doesn't implement the Copy trait). This solution is far from complete.

Copy link
Member

Choose a reason for hiding this comment

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

@GuillaumeGomez That's only because the & is removed altogether, allowing you as the owner to relinquish the ownership (move it into foo()).

The foo(*r_s) suggestion is still wrong, it will trigger E0507 (cannot move out of borrowed content).

Copy link
Member Author

Choose a reason for hiding this comment

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

That's precisely what I said:

For this case, I'd need to check if it's actually possible to deref without error but I don't know how to do it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the cases are:

  • type is Copy -> good
  • value is an index operation -> bad
  • value is a tuple or struct field op -> OK if root value is not a reference
  • everything else -> ok

if expected_ty == &checked_ty.ty.sty {
let sp = self.sess().codemap().call_span_if_macro(expr.span);
if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
return if src.starts_with("&") {
Copy link
Contributor

Choose a reason for hiding this comment

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

You should probably destructure expr to figure out if it's an ExprRef. This way you can obtain the inner item directly. Your current code would suggest mut String::new() for foo(&mut String::new()).

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh damn, excellent point!

@@ -239,6 +239,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
None
}
(expected_ty, &ty::TyRef(_, checked_ty)) => {
if expected_ty == &checked_ty.ty.sty {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be cool if this would infinitely deref, so we'd get suggestions for &&T when T was expected, or even &str when starting out with a String.

Copy link
Member Author

Choose a reason for hiding this comment

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

I can extract the "real" type through all references. I'll do it next.

Copy link
Contributor

Choose a reason for hiding this comment

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

using == is a bit strict. We might want to use instead infcx.can_sub(check_ty, expected_ty).

@arielb1
Copy link
Contributor

arielb1 commented Aug 15, 2017

r? @eddyb - not sure how much we want this.

@arielb1 arielb1 added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Aug 15, 2017
@eddyb
Copy link
Member

eddyb commented Aug 16, 2017

I do not want to review any more suggestion hacks. r? @nikomatsakis

@aidanhs
Copy link
Member

aidanhs commented Aug 24, 2017

I've pinged @nikomatsakis on IRC about this PR.

(expected_ty, &ty::TyRef(_, checked_ty)) => {
if expected_ty == &checked_ty.ty.sty {
let sp = self.sess().codemap().call_span_if_macro(expr.span);
if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think processing the snippets is really fragile. Imo it would be much cleaner to destructure expr and check for ExprAddrOf

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point!

|
= note: expected type `std::string::String`
found type `&std::string::String`
= help: try with `*r_s`
Copy link
Contributor

Choose a reason for hiding this comment

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

The suggestion is still wrong. I think this should only trigger for expressions that produce a temporary or Copy types. Everything else should be handled by the method suggesting code that gets triggered on e.g. let u: usize = ""; producing a suggesting for .len().

|
= note: expected type `std::string::String`
found type `&std::string::String`
= help: try with `*r_s`
Copy link
Contributor

@nikomatsakis nikomatsakis Aug 24, 2017

Choose a reason for hiding this comment

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

so I think that the overall code is fine -- if a bit restrictive -- but this is kind of a goofy suggestion, right? i.e., it's going to lead to a move error. Perhaps we should check if the type is copy before offering it? You can do this via: infcx.type_moves_by_default(self.param_env, ty, span)

Copy link
Member Author

Choose a reason for hiding this comment

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

I wanted to but was looking for the function to do so. Thanks!

Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

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

Looks like a good start. Sorry for the delay @GuillaumeGomez

@carols10cents carols10cents added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 28, 2017
@arielb1
Copy link
Contributor

arielb1 commented Aug 29, 2017

I think @nikomatsakis wants the Copy fix?

@GuillaumeGomez
Copy link
Member Author

Yes yes, still in my todo list.

}
}
_ => {
if self.infcx.type_moves_by_default(self.param_env,
Copy link
Member Author

Choose a reason for hiding this comment

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

@nikomatsakis: This call is never returning true so I suppose I'm misusing it. At first, I checked on checked_ty directly but then I thought that the fact it was a reference might make it not work. Then I tried on the lower level (which is the current code) but still can't make it work. Any idea what I'm doing wrong?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you want !self.infcx.type_moves_by_default -- that is, this function returns false is the value is Copy (and hence it does not move).

}
}
_ => {
if self.infcx.type_moves_by_default(self.param_env,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you want !self.infcx.type_moves_by_default -- that is, this function returns false is the value is Copy (and hence it does not move).

@GuillaumeGomez
Copy link
Member Author

Updated.

@nikomatsakis: You were right: it works just as expected now! :)

// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*fn foo(_: String) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this meant to be commented out?

fn main() {
let s = String::new();
let r_s = &s;
foo(r_s);
Copy link
Contributor

Choose a reason for hiding this comment

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

can you add some comments explaining the purpose of each of these random foo functions?

e.g. "// Check that we do not suggest *r_s when the argument type is not copy".

foo(r_s);
foo(&"aaa".to_owned());
foo(&mut "aaa".to_owned());
foo4(&0);
Copy link
Contributor

Choose a reason for hiding this comment

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

I saw some logic around macros -- can we embed this in a macro and see what happens?

Copy link
Contributor

Choose a reason for hiding this comment

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

e.g. foo(borrow!(0)) where:

macro_rules! borrow {
   ($x:expr) => { &$x }
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea!

@GuillaumeGomez
Copy link
Member Author

@nikomatsakis: The error with the macro is funny:

error[E0308]: mismatched types
  --> src/test/ui/deref-suggestion.rs:12:20
   |
12 |     ($x:expr) => { &$x }
   |                    ^^^ expected u32, found &{integer}
...
32 |     foo3(borrow!(0));
   |          ---------- in this macro invocation
   |
   = note: expected type `u32`
              found type `&{integer}`
   = help: try with `0`

error: aborting due to 5 previous errors

match expr.node {
hir::ExprAddrOf(_, ref expr) => {
if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
return Some(format!("try with `{}`", code));
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess @GuillaumeGomez that this particular macro is triggering this case. Try having the macro produce {&x} for example -- although this is also an interesting data point.

@arielb1
Copy link
Contributor

arielb1 commented Sep 19, 2017

What's the status of this PR? Are you blocked on something @GuillaumeGomez? Just a friendly ping to make sure this isn't getting lost.

@GuillaumeGomez
Copy link
Member Author

I'm still trying to figure out how I can "fix" the problem when there are macros.

@oli-obk
Copy link
Contributor

oli-obk commented Sep 20, 2017

You can only produce the suggestion if span.ctxt().outer().expn_info().is_none(). Around macros it is nigh impossible to create sensible suggestions

@nikomatsakis
Copy link
Contributor

Yeah, I also had in mind just disabling around macros. (Thanks @oli-obk, I wasn't sure the best way to check for that.)

@GuillaumeGomez
Copy link
Member Author

Ah perfect. I'll update soon then. Thanks @oli-obk!

@GuillaumeGomez
Copy link
Member Author

Updated. Now, when the issue is in a macro, the help isn't displayed anymore.

- .escape_default()
- .escape_unicode()
- .to_lowercase()
- .to_uppercase()
Copy link
Contributor

Choose a reason for hiding this comment

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

Unrelated, but we really have to do something here. These suggestions are pretty silly.

@nikomatsakis
Copy link
Contributor

I added some comments explaining the code a bit. Looks good to me now! Thanks @GuillaumeGomez

@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Collaborator

bors commented Sep 22, 2017

📌 Commit 2f52282 has been approved by nikomatsakis

@bors
Copy link
Collaborator

bors commented Sep 22, 2017

⌛ Testing commit 2f52282d2267730d6e6ce506ee0dab528de0e33d with merge 11ffb6801bb2aed4986ac17f013153593d7bb3cf...

@bors
Copy link
Collaborator

bors commented Sep 22, 2017

💔 Test failed - status-appveyor

return Some(format!("try with `{}`", code));
}
}

Copy link
Member

Choose a reason for hiding this comment

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

tidy error: C:\projects\rust\src\librustc_typeck\check\demand.rs:281: trailing whitespace

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah damn, thanks!

@GuillaumeGomez
Copy link
Member Author

I fixed the tidy issue so let's start it over!

@bors: r=nikomatsakis

@bors
Copy link
Collaborator

bors commented Sep 23, 2017

📌 Commit 21d4ba2 has been approved by nikomatsakis

@bors
Copy link
Collaborator

bors commented Sep 23, 2017

⌛ Testing commit 21d4ba2 with merge a83c3e7...

bors added a commit that referenced this pull request Sep 23, 2017
@bors
Copy link
Collaborator

bors commented Sep 23, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: nikomatsakis
Pushing a83c3e7 to master...

@bors bors merged commit 21d4ba2 into rust-lang:master Sep 23, 2017
@GuillaumeGomez GuillaumeGomez deleted the deref-suggestion branch September 23, 2017 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.