-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Suggest explicit lifetime parameter #12238
Conversation
Exciting! I'll take a look asap |
Awesome. I think this will be very helpful. We should start collecting error messages that could benefit from simple suggestions like this... suggesting |
This semester has turned out to be rather busy, so I won't be able to work on this for a week. Feel free to close this PR to clear up bors's queue. @bstrie: I plan to look into the "cannot move out of dereference of {@, &} pointer" error once I'm done with this one (since I hate that error with a passion). @nikomatsakis: I'd like some feedback on an approach I plan to take. When both fn foo<'a, 'b>(x: &'a Foo) -> (&'b int, &'a int) {
(&x.bar, &x.bar)
} it would be wrong to replace What I plan to do is, first, collect all the SubSupConflict where So for the following function, fn foo<'a, 'b>(x: &'a Foo) -> (&'b int, &'a int, &int) {
(&x.bar, &x.bar, &x.bar)
} we would get |
@@ -231,6 +232,12 @@ pub fn ident_to_pat(id: NodeId, s: Span, i: Ident) -> @Pat { | |||
span: s } | |||
} | |||
|
|||
pub fn ident_to_dummy_lifetime(ident: Ident) -> Lifetime { | |||
Lifetime { id: 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.
Shouldn't that use DUMMY_NODE_ID
instead?
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.
Hmm, turns out DUMMY_NODE_ID
is -1. I actually just copied that code snippet from a pprust test, and assumed that DUMMY_NODE_ID
is 0. :P
So this pull request looks pretty good! This is very close to what I had in mind. I am pleased we were able to do it purely in error reporting. I suspect that it would also be good to investigate and improve the code that selects what inputs to provide to error reporting as well, but that's a separate question. @ktt3ja as far as your question concerning a more advanced example, I don't necessarily think we need to solve that particular problem, but I'll not that it's not necessarily wrong to suggest replacing I think the best thing to do would be to cleanup this patch a bit (for example, to find a good lifetime name, perhaps search through |
@nikomatsakis: Ah oops. Somehow I missed the notification for your comment until now. Since I had already started working on handling named lifetimes and multiple lifetimes, I think I'll try finishing that up first. |
@nikomatsakis: I just finished putting my plan from two comments back into implementation. It turned out to be a lot longer than I expected. It's also a bit ugly because of all the clones. :( The way I handle named lifetimes is rather naive at the moment, so I chose to not support methods for now. At least it can suggest something like this, though: struct Foo<'x, 'y, 'z> { bar: &'y int, baz: int }
fn foo(x: &Foo) -> (&int, &int, &int) {
(x.bar, &x.baz, &x.baz)
}
test.rs:24:13: 24:19 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:24 (x.bar, &x.baz, &x.baz)
^~~~~~
test.rs:24:21: 24:27 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:24 (x.bar, &x.baz, &x.baz)
^~~~~~
test.rs:24:5: 24:28 error: mismatched types: expected `(&int,&int,&int)` but found `(&int,&int,&int)` (lifetime mismatch)
test.rs:24 (x.bar, &x.baz, &x.baz)
^~~~~~~~~~~~~~~~~~~~~~~
note: consider using an explicit lifetime parameter as shown:
fn foo<'a, 'b, 'c, 'd>(x: &'d Foo<'b, 'a, 'c>) -> (&'a int, &'d int, &'d int)
error: aborting due to 3 previous errors and for the same function, but with some lifetimes already defined: fn foo<'a, 'b, 'c>(x: &Foo<'a, 'b, 'c>) -> (&int, &int, &int) {
(x.bar, &x.baz, &x.baz)
}
test.rs:28:13: 28:19 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:28 (x.bar, &x.baz, &x.baz)
^~~~~~
test.rs:28:21: 28:27 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:28 (x.bar, &x.baz, &x.baz)
^~~~~~
test.rs:28:5: 28:28 error: mismatched types: expected `(&int,&int,&int)` but found `(&'b int,&int,&int)` (lifetime mismatch)
test.rs:28 (x.bar, &x.baz, &x.baz)
^~~~~~~~~~~~~~~~~~~~~~~
note: consider using an explicit lifetime parameter as shown:
fn foo<'d, 'e, 'a, 'c>(x: &'e Foo<'a, 'd, 'c>) -> (&'d int, &'e int, &'e int) I don't know how to add test for non-span note, but along with the above here are the ones I have tried so far (with simpler Foo struct): struct Foo<'x> { bar: int }
fn foo(x: &Foo) -> &int {
&x.bar
}
test.rs:3:5: 3:11 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:3 &x.bar
^~~~~~
note: consider using an explicit lifetime parameter as shown:
fn foo<'a>(x: &'a Foo) -> &'a int
error: aborting due to previous error fn foo<'a, 'b>(x: &'a Foo) -> &'b int {
&x.bar
}
test.rs:7:5: 7:11 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:7 &x.bar
^~~~~~
note: consider using an explicit lifetime parameter as shown:
fn foo<'c>(x: &'c Foo) -> &'c int
error: aborting due to previous error fn foo(x: &Foo) -> (&int, &int) {
(&x.bar, &x.bar)
}
test.rs:11:6: 11:12 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:11 (&x.bar, &x.bar)
^~~~~~
test.rs:11:14: 11:20 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:11 (&x.bar, &x.bar)
^~~~~~
note: consider using an explicit lifetime parameter as shown:
fn foo<'a>(x: &'a Foo) -> (&'a int, &'a int)
error: aborting due to 2 previous errors fn foo<'a, 'b>(x: &'a Foo) -> (&'b int, &'a int, &int) {
(&x.bar, &x.bar, &x.bar)
}
test.rs:15:6: 15:12 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:15 (&x.bar, &x.bar, &x.bar)
^~~~~~
test.rs:15:22: 15:28 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:15 (&x.bar, &x.bar, &x.bar)
^~~~~~
note: consider using an explicit lifetime parameter as shown:
fn foo<'c>(x: &'c Foo) -> (&'c int, &'c int, &'c int)
error: aborting due to 2 previous errors fn foo(x: &int) -> &int {
x
}
test.rs:19:5: 19:6 error: cannot infer an appropriate lifetime due to conflicting requirements
test.rs:19 x
^
test.rs:19:5: 19:6 error: mismatched types: expected `&int` but found `&int` (lifetime mismatch)
test.rs:19 x
^
note: consider using an explicit lifetime parameter as shown:
fn foo<'a>(x: &'a int) -> &'a int
error: aborting due to 2 previous errors It seems to work fine on these functions. I'm still worried I may have made some wrong assumption along the way, though, since getting this to work was a little painful. |
@@ -662,3 +1197,61 @@ impl Resolvable for @ty::TraitRef { | |||
} | |||
} | |||
|
|||
struct LifeGiver { |
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 was very tempted to name this ForestSpirit
since I was watching Princess Mononoke today. :p
The examples look nice. I think we may want to tweak the actual output, and I think we definitely want to give the output a span (ideally the function declaration). Probably we want the suggested fix to come first, before the details. I haven't read over the details yet, will try to do so today! |
@nikomatsakis: When you said it should be a span note, do you mean it should look like this: test.rs:2:25: 4:2 note: consider using an explicit lifetime parameter as shown: fn foo<'a>(x: &'a Foo) -> &'a int
test.rs:2 fn foo(x: &Foo) -> &int { or test.rs:2:25: 4:2 note: consider using an explicit lifetime parameter as shown:
test.rs:2 fn foo<'a>(x: &'a Foo) -> &'a int If it's the latter, I think I would have to do a custom |
@ktt3ja I think I meant the former. |
@nikomatsakis: I changed the note into a span note and put it at the beginning. Unfortunately, the function declaration doesn't have its own span, so the note uses the span of the whole function. test.rs:2:1: 4:2 note: consider using an explicit lifetime parameter as shown: fn foo1<'a>(x: &'a Foo) -> &'a int
test.rs:2 fn foo1(x: &Foo) -> &int {
test.rs:3 &x.bar
test.rs:4 }
test.rs:3:5: 3:11 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
test.rs:3 &x.bar
^~~~~~ I also added some tests. The tests are fragile since changing the way lifetime names are generated will break them, but having some tests is still better than having none. |
@ktt3ja sorry it took a while for me to read this. This is some excellent stuff. I made a bunch of suggestions, mostly pretty minor though, requests for comments and so forth. How much work do you think it is to handle methods? If it seems like a lot, let's open a follow up bug and land without that support. The one thing I would like to do is to add a That said, maybe leave the code commented out or in some way disabled that iterates over and reports more details. I imagine we'll want to tweak this as we go. |
@nikomatsakis: I pushed a new version that addresses most of your comments. I'm unsure how hard it would be to support methods--I would need to run it on some test cases to get a better understanding, and unfortunately I can't work on it right now. I would prefer that this lands for now so that we get bug report for things I may have overlooked. For your last two paragraphs, do you mean that we don't report the type/inference errors at all, and change the suggestion span note into an error? Do you want it to go with this PR? |
@ktt3ja I'm thinking now let's just land the PR with the error messages as they are (which seems like a strict improvement over what we have) and we can tweak as we go. I'll review one more time. |
r+ but for dead methods |
@nikomatsakis: dead methods have been removed |
You may be interested in the travis failure. |
@ktt3ja looks like you have to replace uses of |
I have replaced all the |
OK. It seems like we'll want to have all the mutation methods available on vec (and only vec, I would think). |
@ktt3ja I think this needs a rebase |
Some types of error are caused by missing lifetime parameter on function or method declaration. In such cases, this commit generates some suggestion about what the function declaration could be. This does not support method declaration yet.
@nikomatsakis: rebase done |
For the following code snippet: ```rust struct Foo { bar: int } fn foo1(x: &Foo) -> &int { &x.bar } ``` This PR generates the following error message: ```rust test.rs:2:1: 4:2 note: consider using an explicit lifetime parameter as shown: fn foo1<'a>(x: &'a Foo) -> &'a int test.rs:2 fn foo1(x: &Foo) -> &int { test.rs:3 &x.bar test.rs:4 } test.rs:3:5: 3:11 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements test.rs:3 &x.bar ^~~~~~ ``` Currently it does not support methods.
feat: Change VSCode extension publisher to `rust-lang`
For the following code snippet:
This PR generates the following error message:
Currently it does not support methods.