-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
Learn to parse a as usize < b
#42578
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
src/libsyntax/parse/parser.rs
Outdated
}; | ||
let path = TyKind::Path(None, path); | ||
let span = lo.to(self.prev_span); | ||
let rhs = P(Ty { node: path, span: span, id: ast::DUMMY_NODE_ID }); |
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 it possible to use mk _ty
here? Or is the DUMMY_NODE_ID
intentional?
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.
mk_ty
is part of TyCtxt
which isn't available yet. This was extracted from parse_ty_common
in order to see if this would work at all. I could modify parse_ty_common
for this case, but I felt it would muddy the logic of that method.
I'd be okay with this if the unbounded lookahead and parser state saving/restoring machinery were used only for reporting better diagnostics, recovery and fix-it suggestions, not for actually accepting the code. |
Looks like there are some test failures in Travis: https://travis-ci.org/rust-lang/rust/jobs/241424157#L1853, though addressing @petrochenkov's concern first seems like a good idea. |
Should Having said that, what would be your preferred output if this were used only for diagnostics? Something like this?
|
Parsing `a as usize > b` always works, but `a as usize < b` was a parsing error because the parser would think the `<` started a generic type argument for `usize`. The parser now attempts to parse as before, and if a DiagnosticError is returned, try to parse again as a type with no generic arguments. If this fails, return the original `DiagnosticError`.
A specially crafted error would be the best, IMO.
After this is reported as a non-fatal error, AST is build as if |
It's an edge case, but it has large effect on properties of the grammar as a whole. |
|
``` warning: `<` is interpreted as a start of generic arguments for `usize`, not comparison --> $DIR/issue-22644.rs:16:33 | 16 | println!("{}", a as usize < b); | ^ expected one of `!`, `(`, `+`, `,`, `::`, or `>` here | help: if you want to compare the casted value then write | println!("{}", (a as usize) < b); ```
e53c7a1
to
803d323
Compare
I understand and completely agree.
I think I prefer the former, as it'd be less surprising for a newcomer. I got something working which accepts the code as proposed but also emits a warning suggesting a correction.
This is how it looks like right now:
|
src/libsyntax/parse/parser.rs
Outdated
@@ -438,6 +441,8 @@ fn dummy_arg(span: Span) -> Arg { | |||
Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID } | |||
} | |||
|
|||
type RewindPoint = (token::Token, Span, Option<Span>, Span, TokenCursor, Vec<TokenType>); |
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.
Could you make this a struct with field names?
``` warning: `<` is interpreted as a start of generic arguments for `usize`, not a comparison --> $DIR/issue-22644.rs:16:33 | 16 | println!("{}", a as usize < b); | - ^ interpreted as generic argument | | | not interpreted as comparison | help: if you want to compare the casted value then write: | println!("{}", (a as usize) < b); ```
As long as we're not changing the grammar, I'm happy if @petrochenkov is happy =) |
@nikomatsakis just to be clear, the code as is accepts it under the assumption that it is correct and only emits a |
src/libsyntax/parse/parser.rs
Outdated
@@ -1819,7 +1850,8 @@ impl<'a> Parser<'a> { | |||
} | |||
|
|||
// Parse types, optionally. | |||
let parameters = if self.eat_lt() { | |||
let parameters = if self.is_lt() && !dont_parse_generics { |
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.
if !dont_parse_generics && self.eat_lt() {
&&
doesnt run second arg when first arg is 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.
👍
This also makes fn is_lt
unnecessary
EDIT: ... and keeps <
in the expected set.
Accepts what exactly? And what do you mean by "as is"? You mean, in the PR, or in master? |
@nikomatsakis The PR accepts |
src/libsyntax/parse/parser.rs
Outdated
@@ -1800,7 +1829,9 @@ impl<'a> Parser<'a> { | |||
/// - `a::b<T,U>::c<V,W>` | |||
/// - `a::b<T,U>::c(V) -> W` | |||
/// - `a::b<T,U>::c(V)` | |||
pub fn parse_path_segments_without_colons(&mut self) -> PResult<'a, Vec<PathSegment>> { | |||
pub fn parse_path_segments_without_colons(&mut self, dont_parse_generics: bool) |
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: dont_parse_generics: bool
-> parse_generics: bool
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.
Done.
src/libsyntax/parse/parser.rs
Outdated
ExprKind::Cast(lhs, rhs), ThinVec::new()); | ||
} | ||
Err(mut err) => { | ||
// Rewind to before attempting to parse the type with generics, to get |
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.
Could you move the error handling logic into a separate 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.
Done.
Huh, I'm confused. After all, you can do |
r=me with stray note removed. |
- generate error instead of warning - remove `RewindPoint` and just keep a copy of `Parser` to rewind state. - `dont_parse_generics: bool` -> `parse_generics: bool` - remove `eat_lt` - move error handling code to separate method
@petrochenkov done @bors r=petrochenkov |
📌 Commit ad260ff has been approved by |
@bors r- |
I'd like to know if this is changing the grammar that is accepted. I feel like I still don't understand quite what's happening here. =) |
In particular, do we now compile (successfully) more programs than we did before? If so, what set of programs? |
@nikomatsakis The first commit of this PR changed the accepted grammar and only emitted a warning. I changed it back to not have any changes in the grammar and emit an error. The parser state changes slightly after finding this case in order to continue parsing successfully the rest of the file (before this PR the rest of the file would not get any diagnostics). No new set of programs will be accepted, the new behaviors are:
|
@estebank perfect =) |
@bors r+ |
📌 Commit ad260ff has been approved by |
Learn to parse `a as usize < b` Parsing `a as usize > b` always works, but `a as usize < b` was a parsing error because the parser would think the `<` started a generic type argument for `usize`. The parser now attempts to parse as before, and if a DiagnosticError is returned, try to parse again as a type with no generic arguments. If this fails, return the original `DiagnosticError`. Fix #22644.
☀️ Test successful - status-appveyor, status-travis |
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.
One last thing: error: `<` is interpreted as a start of generic arguments for `u16`, not a comparison
--> /tmp/angle.rs:4:29
|
4 | println!("{}", x as u16 << 4);
| -------- ^^ - interpreted as generic arguments
| | |
| | not interpreted as comparison
| help: try comparing the casted value: `(x as u16)` |
@Hertz4
|
Parsing
a as usize > b
always works, buta as usize < b
was aparsing error because the parser would think the
<
started a generictype argument for
usize
. The parser now attempts to parse as before,and if a DiagnosticError is returned, try to parse again as a type with
no generic arguments. If this fails, return the original
DiagnosticError
.Fix #22644.