Skip to content
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

[CP] Patterns parsing: fix ambiguity resolution for when and as. #52215

Closed
stereotype441 opened this issue Apr 28, 2023 · 0 comments
Closed

[CP] Patterns parsing: fix ambiguity resolution for when and as. #52215

stereotype441 opened this issue Apr 28, 2023 · 0 comments
Assignees
Labels
cherry-pick-approved Label for approved cherrypick request cherry-pick-merged Cherry-pick has been merged to the stable or beta branch. cherry-pick-review Issue that need cherry pick triage to approve legacy-area-fe-analyzer-shared Legacy: Use area-dart-model instead. merge-to-beta

Comments

@stereotype441
Copy link
Member

stereotype441 commented Apr 28, 2023

Commit(s) to merge

637dd76

Target

Beta

Prepared changelist for beta/stable

https://dart-review.googlesource.com/c/sdk/+/299700

Issue Description

When encountering a guarded pattern of the form case IDENTIFIER when !EXPRESSION: or case IDENTIFIER.IDENTIFIER when !EXPRESSION, the parser incorrectly interprets the IDENTIFIER(s) as a type name, the when as an ordinary identifier, and the ! as a null assert pattern; then it considers the EXPRESSION part to be unexpected text and rejects it with an error.

What is the fix

The parser disambiguation rules have been adjusted so that when encountering a pattern of the form T when or T as, where T could be parsed as a valid type name, it does not try to parse the construct as a typed variable pattern (with T denoting the type and the when or as token denoting the variable name). Instead it falls back on an interpretation of the pattern where T is not a type. This could be:

  • An identifier pattern or record pattern (if T begins with an identifier)
  • A record pattern or parenthesized pattern (if T begins with ()

In addition to fixing the bug, this means, in effect, that typed variable patterns are no longer allowed to declare variables named when or as.

In a future release we intend to prohibit all variable and identifier patterns from declaring variables named when or as, however for this cherry-pick request, we're restricting the change to just what's necessary to fix the bug.

Why cherry-pick

Since guard expressions (the expressions after when in a guarded pattern) are boolean expressions, and ! is a boolean operator, guard expressions beginning with ! are likely to be common. Also, patterns of the form IDENTIFIER and IDENTIFIER.IDENTIFIER are likely to be comon (because these forms were both valid in switch cases prior to the introduction of patterns). So it seems likely that this issue will be triggered by user code fairly often. Without this fix, the user will see a fairly confusing parse error. The error can in principle be worked around by placing parentheses around the IDENTIFIER(s), but this is in no way obvious.

The fix is very targeted: the affected code path only triggers when a pattern is seen of the form T when or T as, where T is a sequence of tokens that could be validly parsed as a type, and the only behaviour change is to avoid ever interpreting such a token sequence as a variable declaration. It's extremely rare for a user to deliberately name a variable as (due to the meaning of as as a keyword); with the introduction of patterns it's likely that it will become equally rase for a user to deliberately name a variable when. So it's highly unlikely that a user will ever write new code in which the when or as token is expected to be interpreted as a variable name. As for existing (pre-Dart-3.0) code that is being ported to Dart 3.0, @munificent checked a big corpus of pub packages, open source Flutter apps, open source Flutter widgets, the Dart repo, and the Flutter repo, and found zero switch cases containing the identifier when (see #52199 (comment)).

Risk

low

Issue link(s)

#52199

Extra Info

No response

@stereotype441 stereotype441 added the cherry-pick-review Issue that need cherry pick triage to approve label Apr 28, 2023
@mraleph mraleph added the legacy-area-fe-analyzer-shared Legacy: Use area-dart-model instead. label Apr 28, 2023
@itsjustkevin itsjustkevin added merge-to-beta cherry-pick-approved Label for approved cherrypick request labels May 1, 2023
copybara-service bot pushed a commit that referenced this issue May 1, 2023
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>
@itsjustkevin itsjustkevin added the cherry-pick-merged Cherry-pick has been merged to the stable or beta branch. label May 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cherry-pick-approved Label for approved cherrypick request cherry-pick-merged Cherry-pick has been merged to the stable or beta branch. cherry-pick-review Issue that need cherry pick triage to approve legacy-area-fe-analyzer-shared Legacy: Use area-dart-model instead. merge-to-beta
Projects
None yet
Development

No branches or pull requests

7 participants