Skip to content

Commit faab516

Browse files
committed
Python 3.10+: handle match/case captures in scopeanalyzer
1 parent aa9fccf commit faab516

File tree

1 file changed

+30
-1
lines changed

1 file changed

+30
-1
lines changed

unpythonic/syntax/scopeanalyzer.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
from mcpyrate.core import Done
8484
from mcpyrate.walkers import ASTTransformer, ASTVisitor
8585

86-
from .astcompat import TryStar
86+
from .astcompat import TryStar, MatchStar, MatchMapping, MatchClass, MatchAs
8787

8888
from ..it import uniqify
8989

@@ -313,6 +313,12 @@ def get_names_in_store_context(tree):
313313
by ``get_lexical_variables`` for the nearest lexically surrounding parent
314314
tree that represents a scope.
315315
"""
316+
class MatchCapturesCollector(ASTVisitor): # Python 3.10+: `match`/`case`
317+
def examine(self, tree):
318+
if type(tree) is Name:
319+
self.collect(tree.id)
320+
self.generic_visit(tree)
321+
316322
class StoreNamesCollector(ASTVisitor):
317323
# def _collect_name_or_list(self, t):
318324
# if type(t) is Name:
@@ -346,6 +352,29 @@ def examine(self, tree):
346352
# TODO: `try`, even inside the `except` blocks, will be bound in the whole parent scope.
347353
for h in tree.handlers:
348354
self.collect(h.name)
355+
# Python 3.10+: `match`/`case` uses names in `Load` context to denote captures.
356+
# Also there are some bare strings, and sometimes `None` actually means "_" (but doesn't capture).
357+
# So we special-case all of this.
358+
elif type(tree) in (MatchAs, MatchStar): # a `MatchSequence` also consists of these
359+
if tree.name is not None:
360+
self.collect(tree.name)
361+
elif type(tree) is MatchMapping:
362+
mcc = MatchCapturesCollector(tree.patterns)
363+
mcc.visit()
364+
for name in mcc.collected:
365+
self.collect(name)
366+
if tree.rest is not None: # `rest` is a capture if present
367+
self.collect(tree.rest)
368+
elif type(tree) is MatchClass:
369+
mcc = MatchCapturesCollector(tree.patterns)
370+
mcc.visit()
371+
for name in mcc.collected:
372+
self.collect(name)
373+
mcc = MatchCapturesCollector(tree.kwd_patterns)
374+
mcc.visit()
375+
for name in mcc.collected:
376+
self.collect(name)
377+
349378
# Python 3.12+: `TypeAlias` uses a name in `Store` context on its LHS so it needs no special handling here.
350379

351380
# Same note as for for loops.

0 commit comments

Comments
 (0)