-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Try to recover from unmatched delimiters in the parser #54029
Try to recover from unmatched delimiters in the parser #54029
Conversation
src/libsyntax/parse/parser.rs
Outdated
mut err: DiagnosticBuilder<'a>, | ||
) -> PResult<'a, ()> { | ||
let mut pos = None; | ||
let mut tokens: Vec<token::Token> = tokens.iter().map(|t| (*t).clone()).collect(); |
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.
isn't that just tokens.to_vec()
?
sess: &ParseSess, | ||
override_span: Option<Span>, | ||
) -> TokenStream { | ||
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span).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.
does this mean that for rustc invocations that don't read the source from a file but from stdin we don't get these suggestions?
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.
Correct. On the one hand, those will not have any worse output. On the other, this was a bit explorative and want to nail it down before trying to get to to work in all cases.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
7a222ae
to
0036e72
Compare
//~| ERROR expected expression | ||
fs::create_dir_all(path.as_ref()).map(|()| true) | ||
} else { | ||
//~^ ERROR incorrect close delimiter: `}` | ||
Ok(false); | ||
} |
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.
What would be the ideal output for this file?
I'll get back to this over the weekend to see if I can make the parser recover from missing delimiters and parse the rest correctly, so that we get the type error about fs::create_dir_all
back.
@nikomatsakis ready for a proper review. |
This comment has been minimized.
This comment has been minimized.
2962e35
to
8605d87
Compare
src/libsyntax/parse/parser.rs
Outdated
// - - ^ expected one of `)`, <...> | ||
// | | | ||
// | help: ...missing `)` might belong here | ||
// you might have meant to close this... |
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 really the right spot? I would have suggested us to suggest adding the )
after the {}
, like so {foo(bar {})}
, not {foo(bar) {}}
, which doesn't look to me like it would parse
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.
Now I'm intrigued on wether the comment is correct, at all...
I'd like to create a bunch of real "broken" code samples to test against, which would give me more confidence that what I'm doing in this PR is beneficial.
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 would help me too, since I'm starting my reviews by just looking at the test cases (I haven't really looked at the code at all yet)
LL | } | ||
| ^ incorrect close delimiter | ||
|
||
error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `{` |
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.
it's kind of surprising to me that this advice comes here, in a second error after the "main" error above... why is that?
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 lexer first performs the basic matching braces checks and emits the appropriate error. Then the parser takes the tokenstream and tries to parse things. So the first error is about the lexer finding unbalanced delimiters, while the second one is a parse error not finding an expected close delimiter.
There're two options: keeping the un-emitted DiagnosticBuilders from the lexer and emitting them once we are parsing and find an appropriate place for the close delimiter suggestion, or completely stop performing balanced delimiter checks in the lexer and rely entirely on the parser for parse errors. Both felt like too much work for little win. I'd prefer to iterate this PR until we have the recovery nailed down before revisiting the above possibilities.
LL | } | ||
| ^ expected expression | ||
|
||
error[E0401]: can't use type parameters from outer function |
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.
still, nice that we recovered here
| | help: ...the missing `)` may belong here | ||
| if you meant to close this... | ||
|
||
error: expected expression, found `)` |
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.
"found )
" is confusing too...?
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.
Mmm... This is surprising. I'm not sure why the parser is finding a )
at all here...
LL | } | ||
| ^ incorrect close delimiter | ||
|
||
error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `bar` |
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.
something feels..inelegant to me here. Like, I would like to be suppressing these "fallout" errors, and just picking up parsing from some other point..? Hmm, I'm not sure.
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 is an option, to just bail on parsing the entire block from that point on, as we do in other parts of the parser.
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 problem with out right bailing until the next closing brace is that we then don't get the nice recovery shown above. Maybe that's a reasonable trade-off...
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.
which is the nice recovery you are referring to?
I tend to think that giving "false errors" is kind of the most confusing thing we can do and we should bias in favor of too few errors and not too many, but opinions vary.
LL | foo(x | ||
| - expected one of `.`, `;`, `?`, `}`, or an operator here | ||
LL | bar() | ||
| ^^^ unexpected token |
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 the ideal end state, this error would not get emitted, instead having the prior one be
error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `bar`
--> $DIR/parser-recovery-4.rs:17:5
|
LL | foo(x
| - -- help: you might also be missing a semicolon here: `;`
| | |
| | expected one of 8 possible tokens here
| | help: ...the missing `)` may belong here
| if you meant to close this...
LL | bar()
| ^^^ unexpected token
but I'm not sure how to verify that we don't start suggesting bogus code that wouldn't work...
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'm not sure I understand. But I do think that this example you just gave is kind of cluttered -- I feel like it'd be better to break it apart into multiple sections or something.
@estebank are you planning to make changes here? |
@nikomatsakis yes. I have a couple of alternative approaches that I'm going to try out once I have spare time. Rough timeline, next 2 weeks at the latest. |
OK. |
☔ The latest upstream changes (presumably #54969) made this pull request unmergeable. Please resolve the merge conflicts. |
Ping from triage @estebank: What is the status of this PR? |
@TimNN I'll close to clear the backlog and reopen once I get back to it. |
Follow up to #53949.