-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Parser confused by prefix "!" in pattern guard #52199
Comments
Note that the type name doesn't have to be qualified. A simpler repro is: case foo when !flag: |
Ok, I see the problem. Taking So what it currently does is:
The place where this breaks down is in the third bullet: if the token that follows the supposed type is
I thought I was being so clever when I came up with the disambiguation technique described in the bullets above. Now I wish I had done something like @munificent's suggestion:
However, given the late hour, I'm inclined to suggest a more targeted fix, where we simply change the disambiguation rule to:
(The italicized part is the change). I'll work on a CL that does this. I'm happy to discuss further if folks feel like this is the wrong approach. |
The targeted fix sounds fine to me. By "assume the parser is looking at an identifier pattern" does that mean that if the assumption turns out to be false then it will still treat So given: case foo when: Will it parse that as a variable pattern of type For 3.1, I'm definitely interested in shipping a (probably language versioned) change that says that |
I just checked a big corpus of pub packages, open source Flutter apps, open source Flutter widgets, the Dart repo, and the Flutter repo, and I found zero switch cases containing the identifier |
Nope! That's the disadvantage of our event-based parser architecture: once you decide on an interpretation and start consuming tokens, you can't go back and change your mind. So: case foo when: will become a parse error.
SGTM! I'll get to work on the fix now. |
That's 100% fine with me. No existing pre-Dart 3.0 code should be broken by this (since no non-pattern switch case constant expressions syntactically look like variable patterns) and I'm fine with saying that you can't use |
Just to be clear: the change I'm contemplating also prohibits var (int when, int as) = foo(); // ERROR: your variable names are bad and you should feel bad! I could make the fix only apply in refutable contexts, but it would require some additional plumbing work (and thus be more risky). |
That's also fine. There is no pre-Dart 3.0 code that contains irrefutable patterns, so it's non-breaking. And I don't see any positive value in allowing |
I went ahead and checked the head commits of the Dart SDK and Flutter repos and I don't see any uses of I did run into many uses of
As long as the parser doesn't parse all assignments as pattern assignments, this should be OK. I also don't see any uses of |
Fix is out for review: https://dart-review.googlesource.com/c/sdk/+/299400 |
This change fixes parsing of case clauses such as: case foo when !flag: Constructions like this require some lookahead in order to parse correctly, because the token `when` is valid both as an identifier and as a part of the grammar for a case clause. Therefore, at the time `foo` is encountered, the parser must decide whether it is looking at a variable pattern (`foo when`, where `when` is the name of the variable) or an identifier pattern (`foo`, where `when` begins the case's guard clause). Previous to this fix, the algorithm for disambiguating these two choices was as follows: - If the token sequence starting at `foo` looked like a type, and the token that follows was an identifier, the parser assumed it was looking at a variable pattern with a type; otherwise it assumed it was looking at an identifier pattern. - EXCEPT that if the token that followed the supposed type was `when` or `as` (both of which are valid identifiers), then it probed further: - If the token that followed `when` or `as` was a token that could legitimately follow a pattern, then it assumed that it was looking at a variable pattern with a type. (The tokens that could legitimately follow a pattern are `,`, `:`, `||`, `&&`, `)`, `}`, `]`, `as`, `when`, `?`, `!`). - Otherwise it assumed that it was looking at an identifier pattern. This didn't fully disambiguate, because the third bullet didn't account for the fact that the tokens `as`, `when`, and `!` could _either_ legitimately follow a pattern _or_ legitimately begin an expression (or, in the case of `when`, a type), therefore constructs like the following were incorrectly parsed: - `case foo when as:` (where `as` is a local boolean variable) - `case foo when when:` (where `when` is a local boolean variable) - `case foo when !flag:` (where `flag` is a local boolean variable) - `case foo as when:` (where `when` is the name of a type) The solution is to simplify the disambiguation logic so that if if the token that follows the supposed type is `when` or `as`, then the parser assumes that it's looking at an identifier pattern, _not_ a typed variable pattern. The consequence of this is that the above four constructions are parsed correctly; however it is no longer possible for a typed variable pattern to name a variable `when` or `as`. For consistency we would like to prohibit _any_ variable pattern from naming a variable `when` or `as`, however to keep this change as small as possible (and reduce the risk involved in a possible cherry-pick) that will be postponed until a later CL. Cherry-pick request: #52215 Bug: #52199 Change-Id: I925c70b83bb1ef6efdf0cb2f24ea6186e55079b8 Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/299400 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/299700 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
Bug: #52199 Change-Id: I04fcac4a2b9a7e49a0231441feb764a022dd5ab8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/300320 Reviewed-by: Bob Nystrom <rnystrom@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
In https://dart-review.googlesource.com/c/sdk/+/299400, the parser was adjusted so that it no longer accepts `when` and `as` as the names for variable patterns in cases where there is a possible ambiguity (e.g. `int when` is not accepted as a pattern because `int` is a legitimate pattern, therefore `when` could introduce a guard clause). This change further prohibits `when` and `as` from being the names of variable patterns or identifier patterns even in the case where there is no ambiguity. This is in line with the discussion at #52199 (comment), and the spec change at dart-lang/language#3033. Three new error codes are introduced, to cover the three circumstances in which `when` or `as` might be used illegally: in a declared variable pattern, in an assigned variable pattern, or in an identifier pattern. I've also added analyzer tests to ensure that the parser recovers from these errors nicely. Unfortunately, nice error recovery is only feasible in the non-ambiguous cases. I've also updated the language test expectations in `tests/language/patterns/version_2_32_changes_error_test.dart` to reflect the new error messages, and added a few more examples of uses of `when` and `as` that are still permitted. Fixes #52260. Bug: #52260 Change-Id: I229f627aa639659c30b83c74895759207da279f7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/301482 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Jens Johansen <jensj@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
… when/as. In https://dart-review.googlesource.com/c/sdk/+/299400, the parser was adjusted so that it no longer accepts `when` and `as` as the names for variable patterns in cases where there is a possible ambiguity (e.g. `int when` is not accepted as a pattern because `int` is a legitimate pattern, therefore `when` could introduce a guard clause). This change further prohibits `when` and `as` from being the names of variable patterns or identifier patterns even in the case where there is no ambiguity. This is in line with the discussion at #52199 (comment), and the spec change at dart-lang/language#3033. Three new error codes are introduced, to cover the three circumstances in which `when` or `as` might be used illegally: in a declared variable pattern, in an assigned variable pattern, or in an identifier pattern. I've also added analyzer tests to ensure that the parser recovers from these errors nicely. Unfortunately, nice error recovery is only feasible in the non-ambiguous cases. I've also updated the language test expectations in `tests/language/patterns/version_2_32_changes_error_test.dart` to reflect the new error messages, and added a few more examples of uses of `when` and `as` that are still permitted. Fixes: #52311 Bug: #52260 Change-Id: I30cb3a1ca627c6db3d281740fb59de74c4118c2b Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/301482 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/302100 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Jens Johansen <jensj@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
Consider:
The intent is that this is matching the constant
Enum.value
whenflag
isfalse
. However, the compiler reports:What it thinks it is parsing is:
That is, a null-assert pattern whose inner pattern is
Enum.value when
. The latter is then interpreted as a variable pattern whose name iswhen
and whose type is the prefixed nameEnum.value
.After that, it sees an unexpected identifier
flag
and doesn't know what to do. This is a parser bug because there is an unambiguous way to parse this, which is to treatwhen
as a contextual keyword.But, more broadly, we should probably specify explicitly that
when
can't be used as an identifier in a pattern at all to avoid subtle or complex parsing.The text was updated successfully, but these errors were encountered: