-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[syntax-errors] Multiple assignments in case pattern
#16957
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
Conversation
Summary -- This PR detects multiple assignments to the same name in `case` patterns by recursively visiting each pattern. Test Plan -- New inline tests.
|
MichaReiser
left a comment
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 looks good to me. I've a few smaller suggestions
| let range = if ident.range.start() > other.range.start() { | ||
| ident.range | ||
| } else { | ||
| other.range | ||
| }; |
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 seems that we only need the range comparison here because we visit the PatternMatchAs fields not in the source order. That took me a while to realise. I think I'd prefer to, instead, change visit_pattern so that it visits all fields in source order, so that we can remove the range handling 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 yes, I thought I had tried that first and got surprised when they didn't come out in source order, but I think I just had the order wrong in the MatchAs case.
| impl<'a, Ctx: SemanticSyntaxContext> MultipleCaseAssignmentVisitor<'a, Ctx> { | ||
| /// Check if `other` is present in `self.names` and emit an error if so. | ||
| fn push(&mut self, other: &'a ast::Identifier) { | ||
| for ident in &self.names { |
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 is O(n^2). Should we change the visitor to first collect all names, then sort by name (log (n)), then identify duplicate names by comparing neighboring elements (n)?
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's closer to what I had initially, but I ran into some issues with MatchOr, where the patterns needed to be visited independently. I found a way to work around it and think this is better, thanks!
| 4 | case [y, z, *y]: ... # MatchSequence | ||
| 5 | case [y, y, y]: ... # MatchSequence multiple | ||
| 6 | case {1: x, 2: x}: ... # MatchMapping duplicate pattern | ||
| | ^ Syntax Error: multiple assignments in pattern |
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.
Do you think we should mention the variable name here? It wasn't clear to me from the error message what's wrong in this example: Can I only have a single variable? It might be especially hard to realize what's wrong if the use of the same identifier isn't next to each other ({ 1: x, 2: aaaa_very_long_key, ..., 4: x })
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's a good point. I was trying to avoid putting data into the SemanticSyntaxErrorKind variants, but we can give better error messages if we do. I'll try something more like Python:
>>> match 2:
... case x as x: ...
...
File "<python-input-4>", line 2
case x as x: ...
^^^^^^
SyntaxError: multiple assignments to name 'x' in pattern
>>> match 2:
... case [x, y, *x]: ...
...
File "<python-input-5>", line 2
case [x, y, *x]: ...
^^
SyntaxError: multiple assignments to name 'x' in pattern| } | ||
| } | ||
| Pattern::MatchOr(ast::PatternMatchOr { patterns, .. }) => { | ||
| // each of these patterns should be visited separately |
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 think it would be useful to provide the reason for why should this be done. From what I understand, I think it's because they're separate patterns and checking for duplicate names are isolated to a single pattern.
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 expanded the comment and also moved the relevant test_ok case down.
I also switched to the hash set implementation we discussed yesterday and cleaned up some of the docs and code locations.
|
@ntBre I am seeing an linting error thrown for Any idea what should be happening here? |
|
@sammorley-short Yeah that was a bug in this PR, first reported in #17181. It's been fixed but not released yet. 0.11.4 should come out with the fix today. Thanks for the report! |
Summary
This PR detects multiple assignments to the same name in
casepatterns by recursively visiting each pattern.Test Plan
New inline tests.