-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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 a nicer error message for missing in for loop, fixes #40782. #45639
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @pnkfelix (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
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.
There are some minor changes I'd like, but overall it looks like great! Thank you for taking the time to contribute to the project.
src/libsyntax/parse/parser.rs
Outdated
s.print_pat(&pat)?; | ||
s.s.word(" in ")?; | ||
s.print_expr(&expr) | ||
}); |
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 all of this be format!("for {} in {}", path, expr)
?
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.
Also, it might be nice to try using tcx.sess.codemap().span_to_snippet(expr.span)
and only use s.print_expr(&expr)
if it couldn't be found. This way the expr
is exactly as it was written in the original code, instead of as whatever print_expr
prefers to output (think of cases like for i 0 .. 2 {
, we want to keep the user's formatting).
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 went the easy way and replaced all this with a span_suggestion_short as mentioned below so all this should not apply anymore.
src/libsyntax/parse/parser.rs
Outdated
s.print_expr(&expr) | ||
}); | ||
err.span_suggestion( | ||
in_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.
This span has to cover from the entire suggestion. In this case, you're suggesting for {pat} in {expr}
, so the span should cover from the beginning of the for
span to the end of the expr.span
. This is because suggestions should be able to be applied blindly by other tools to the code, so the replacement should always make sense and have a high degree of confidence (as you do here, because the parsing continued successfully).
Otherwise, instead of pointing at the entire for {pat} {expr}
span, use a span_suggestion_short
, which hides the actual suggestion in the cli output but is still available to code fixing tools like RLS, and suggest only the in
in the span between the end of pat.span
to the start of expr.span
, which would give you the following output:
12 | for i 0..2 {
| ^
| |
| expected `in` here
| help: try adding `in` here
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.
Switched to using span_suggestion_short.
src/libsyntax/parse/parser.rs
Outdated
Ok(expr) => { | ||
// Successfully parsed the expr, print a nice error message. | ||
in_err.cancel(); | ||
let in_span = parser_snapshot_after_in.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.
in_span
should probably be parser_snapshot_after_in.prev_span.end().to(parser_snapshot_after_in.prev_span.span.start())
to point at the empty space between the pat
and the expr
, regardless of how long it is:
12 | for i 0..2 {
| ^^^
| |
| expected `in` here
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, my understanding of spans was indeed pretty poor, hopefully it's getting better. I used between rather than exactly what you suggested but it seems to do the trick.
src/libsyntax/parse/parser.rs
Outdated
|
||
match self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None) { | ||
Ok(expr) => { | ||
// Successfully parsed the expr, print a nice error message. |
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.
Expand the comment with more explanation, stating the reasoning. Something along the lines of "successfully parsed the expr
, which means that the in
kw is missing" and even a simple code sample like for i 0..2 {
.
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've added the example you suggested.
Thanks a lot for the feedback @estebank I just pushed a second commit to address your issues. |
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.
r=me once the suggestion comment has been addressed.
src/libsyntax/parse/parser.rs
Outdated
let mut err = self.sess.span_diagnostic | ||
.struct_span_err(in_span, "missing `in` in `for` loop"); | ||
err.span_label(in_span, "expected `in` here"); | ||
err.span_suggestion_short(in_span, "try adding `in` here", "in".into()); |
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.
One more thing, make the suggestion " in ".into()
, otherwise when applying the suggestion the resulting code would be for iin0..2 {
.
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, should be fixed now.
@bors r+ rollup |
📌 Commit 0d72853 has been approved by |
@@ -3154,13 +3154,47 @@ impl<'a> Parser<'a> { | |||
// Parse: `for <src_pat> in <src_expr> <src_loop_block>` | |||
|
|||
let pat = self.parse_pat()?; | |||
self.expect_keyword(keywords::In)?; |
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 can be done in much simpler way without parser snapshots (please don't overuse the trick from #42578).
if !self.eat_keyword(keywords::In) {
span_err(/*missing `in`*/) // Non-fatal error
}
let expr = self.parse_expr_res(...); // Then parse as usual as if `in` were written
@bors r- |
Thanks for the feedback @petrochenkov I tried adapting the code although I'm not sure I properly understood what you meant. It seems to me that with the new code, the parser will suggest inserting a |
src/libsyntax/parse/parser.rs
Outdated
let in_span = self.prev_span.between(self.span); | ||
let mut err = self.sess.span_diagnostic | ||
.struct_span_err(in_span, "missing `in` in `for` loop"); | ||
err.span_label(in_span, "expected `in` here"); |
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.
Nit: The extra label here creates unnecessary duplication, IMO. The primary message already says that in
is missing and the suggestion is already formatted as a label.
r=me with label removed (#45639 (comment)) |
I just pushed a new commit removing this label, thanks! |
@bors r=petrochenkov rollup |
📌 Commit ed20f3b has been approved by |
⌛ Testing commit ed20f3b with merge 30a4480cb988da9fb1918a13a78a35229e6a0ed6... |
💔 Test failed - status-travis |
@bors retry |
Add a nicer error message for missing in for loop, fixes rust-lang#40782. As suggested by @estebank in issue rust-lang#40782, this works in the same way as rust-lang#42578: if the in keyword is missing, we continue parsing the expression and if this works correctly an adapted error message is produced. Otherwise we return the old error. A specific test case has also been added. This is my first PR on rust-lang/rust so any feedback is very welcome.
As suggested by @estebank in issue #40782, this works in the same way as #42578: if the in keyword is missing, we continue parsing the expression and if this works correctly an adapted error message is produced. Otherwise we return the old error.
A specific test case has also been added.
This is my first PR on rust-lang/rust so any feedback is very welcome.