Skip to content

Commit bb58807

Browse files
Fix parser bug where "type" was misinterpreted as a keyword inside a match (#3950)
Fixes #3790 Slightly hacky, but I think this is correct and it should also improve performance somewhat.
1 parent 722735d commit bb58807

File tree

4 files changed

+33
-1
lines changed

4 files changed

+33
-1
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737

3838
<!-- Changes to the parser or to version autodetection -->
3939

40+
- Fix bug where attributes named `type` were not acccepted inside `match` statements
41+
(#3950)
4042
- Add support for PEP 695 type aliases containing lambdas and other unusual expressions
4143
(#3949)
4244

src/blib2to3/pgen2/parse.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ def __init__(self, grammar: Grammar, convert: Optional[Convert] = None) -> None:
211211
# See note in docstring above. TL;DR this is ignored.
212212
self.convert = convert or lam_sub
213213
self.is_backtracking = False
214+
self.last_token: Optional[int] = None
214215

215216
def setup(self, proxy: "TokenProxy", start: Optional[int] = None) -> None:
216217
"""Prepare for parsing.
@@ -236,6 +237,7 @@ def setup(self, proxy: "TokenProxy", start: Optional[int] = None) -> None:
236237
self.rootnode: Optional[NL] = None
237238
self.used_names: Set[str] = set()
238239
self.proxy = proxy
240+
self.last_token = None
239241

240242
def addtoken(self, type: int, value: str, context: Context) -> bool:
241243
"""Add a token; return True iff this is the end of the program."""
@@ -317,6 +319,7 @@ def _addtoken(self, ilabel: int, type: int, value: str, context: Context) -> boo
317319
dfa, state, node = self.stack[-1]
318320
states, first = dfa
319321
# Done with this token
322+
self.last_token = type
320323
return False
321324

322325
else:
@@ -343,9 +346,23 @@ def classify(self, type: int, value: str, context: Context) -> List[int]:
343346
return [self.grammar.keywords[value]]
344347
elif value in self.grammar.soft_keywords:
345348
assert type in self.grammar.tokens
349+
# Current soft keywords (match, case, type) can only appear at the
350+
# beginning of a statement. So as a shortcut, don't try to treat them
351+
# like keywords in any other context.
352+
# ('_' is also a soft keyword in the real grammar, but for our grammar
353+
# it's just an expression, so we don't need to treat it specially.)
354+
if self.last_token not in (
355+
None,
356+
token.INDENT,
357+
token.DEDENT,
358+
token.NEWLINE,
359+
token.SEMI,
360+
token.COLON,
361+
):
362+
return [self.grammar.tokens[type]]
346363
return [
347-
self.grammar.soft_keywords[value],
348364
self.grammar.tokens[type],
365+
self.grammar.soft_keywords[value],
349366
]
350367

351368
ilabel = self.grammar.tokens.get(type)

tests/data/cases/pattern_matching_complex.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,7 @@
143143
y = 1
144144
case []:
145145
y = 2
146+
# issue 3790
147+
match (X.type, Y):
148+
case _:
149+
pass

tests/data/cases/type_aliases.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
type Alias[T]=lambda: T
66
type And[T]=T and T
77
type IfElse[T]=T if T else T
8+
type One = int; type Another = str
9+
class X: type InClass = int
810

911
type = aliased
1012
print(type(42))
@@ -16,6 +18,13 @@
1618
type Alias[T] = lambda: T
1719
type And[T] = T and T
1820
type IfElse[T] = T if T else T
21+
type One = int
22+
type Another = str
23+
24+
25+
class X:
26+
type InClass = int
27+
1928

2029
type = aliased
2130
print(type(42))

0 commit comments

Comments
 (0)