-
Notifications
You must be signed in to change notification settings - Fork 13.4k
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
Add deref suggestion #43870
Conversation
r? @pnkfelix (rust_highfive has picked a reviewer for you, use r? to override) |
src/test/ui/deref-suggestion.stderr
Outdated
| | ||
= note: expected type `std::string::String` | ||
found type `&std::string::String` | ||
= help: try with `*&"aaa".to_owned()` |
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.
can't it check if &
is already there, or is that too difficult?
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.
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.
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.
c18bcc5
to
b841a43
Compare
src/librustc_typeck/check/demand.rs
Outdated
return if src.starts_with("&") { | ||
Some(format!("try with `{}`", &src[1..])) | ||
} else { | ||
Some(format!("try with `*{}`", &src)) |
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.
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?
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.
@GuillaumeGomez You could check if the target type implements Copy
I guess.
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.
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.
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.
@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).
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.
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.
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 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
src/librustc_typeck/check/demand.rs
Outdated
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("&") { |
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.
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())
.
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.
Oh damn, excellent point!
src/librustc_typeck/check/demand.rs
Outdated
@@ -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 { |
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 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
.
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 can extract the "real" type through all references. I'll do it next.
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.
using ==
is a bit strict. We might want to use instead infcx.can_sub(check_ty, expected_ty)
.
b841a43
to
2bfbd86
Compare
r? @eddyb - not sure how much we want this. |
I do not want to review any more suggestion hacks. r? @nikomatsakis |
I've pinged @nikomatsakis on IRC about this PR. |
src/librustc_typeck/check/demand.rs
Outdated
(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) { |
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 think processing the snippets is really fragile. Imo it would be much cleaner to destructure expr
and check for ExprAddrOf
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.
Good point!
src/test/ui/deref-suggestion.stderr
Outdated
| | ||
= note: expected type `std::string::String` | ||
found type `&std::string::String` | ||
= help: try with `*r_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.
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()
.
src/test/ui/deref-suggestion.stderr
Outdated
| | ||
= note: expected type `std::string::String` | ||
found type `&std::string::String` | ||
= help: try with `*r_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.
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)
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 wanted to but was looking for the function to do so. Thanks!
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.
Looks like a good start. Sorry for the delay @GuillaumeGomez
I think @nikomatsakis wants the |
Yes yes, still in my todo list. |
2bfbd86
to
b8559b1
Compare
src/librustc_typeck/check/demand.rs
Outdated
} | ||
} | ||
_ => { | ||
if self.infcx.type_moves_by_default(self.param_env, |
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.
@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?
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 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).
src/librustc_typeck/check/demand.rs
Outdated
} | ||
} | ||
_ => { | ||
if self.infcx.type_moves_by_default(self.param_env, |
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 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).
b8559b1
to
6db9332
Compare
Updated. @nikomatsakis: You were right: it works just as expected now! :) |
src/test/ui/deref-suggestion.rs
Outdated
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
/*fn foo(_: String) {} |
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.
Is this meant to be commented out?
src/test/ui/deref-suggestion.rs
Outdated
fn main() { | ||
let s = String::new(); | ||
let r_s = &s; | ||
foo(r_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.
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".
src/test/ui/deref-suggestion.rs
Outdated
foo(r_s); | ||
foo(&"aaa".to_owned()); | ||
foo(&mut "aaa".to_owned()); | ||
foo4(&0); |
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 saw some logic around macros -- can we embed this in a macro and see what happens?
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.
e.g. foo(borrow!(0))
where:
macro_rules! borrow {
($x:expr) => { &$x }
}
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.
Good idea!
6db9332
to
201a56a
Compare
@nikomatsakis: The error with the macro is funny:
|
src/librustc_typeck/check/demand.rs
Outdated
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)); |
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 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.
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. |
I'm still trying to figure out how I can "fix" the problem when there are macros. |
You can only produce the suggestion if |
Yeah, I also had in mind just disabling around macros. (Thanks @oli-obk, I wasn't sure the best way to check for that.) |
Ah perfect. I'll update soon then. Thanks @oli-obk! |
201a56a
to
c30435b
Compare
Updated. Now, when the issue is in a macro, the help isn't displayed anymore. |
- .escape_default() | ||
- .escape_unicode() | ||
- .to_lowercase() | ||
- .to_uppercase() |
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.
Unrelated, but we really have to do something here. These suggestions are pretty silly.
I added some comments explaining the code a bit. Looks good to me now! Thanks @GuillaumeGomez |
@bors r+ |
📌 Commit 2f52282 has been approved by |
⌛ Testing commit 2f52282d2267730d6e6ce506ee0dab528de0e33d with merge 11ffb6801bb2aed4986ac17f013153593d7bb3cf... |
💔 Test failed - status-appveyor |
src/librustc_typeck/check/demand.rs
Outdated
return Some(format!("try with `{}`", code)); | ||
} | ||
} | ||
|
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.
tidy error: C:\projects\rust\src\librustc_typeck\check\demand.rs:281: trailing whitespace
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.
Ah damn, thanks!
2f52282
to
21d4ba2
Compare
I fixed the tidy issue so let's start it over! @bors: r=nikomatsakis |
📌 Commit 21d4ba2 has been approved by |
Add deref suggestion Fixes #34562.
☀️ Test successful - status-appveyor, status-travis |
Fixes #34562.