Skip to content

Commit 4da46a6

Browse files
gramsterCopilot
andauthored
Fix match statement issue (#11363)
* Fix pattern matching regression for variadic tuple types When a tuple with an unbounded entry (e.g., tuple[int, *tuple[str, ...], int]) is matched against a pattern shorter than the tuple, the unbounded entry is removed to make lengths match. However, this loses the information that the tuple was originally variadic and could have different lengths. The fix tracks when an unbounded entry is removed (removedIndeterminate flag) and marks such matches as isPotentialNoMatch=true. This prevents the entire variadic tuple from being eliminated in negative narrowing, preserving reachability of subsequent match cases. * Ignore agent files * Address comments --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Graham Wheeler <>
1 parent e2aebd4 commit 4da46a6

3 files changed

Lines changed: 52 additions & 1 deletion

File tree

packages/pyright-internal/src/analyzer/patternMatching.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,7 @@ function getSequencePatternInfo(
14391439

14401440
if (typeArgs.length > patternEntryCount && patternStarEntryIndex === undefined) {
14411441
typeArgs.splice(tupleIndeterminateIndex, 1);
1442+
removedIndeterminate = true;
14421443
tupleIndeterminateIndex = -1;
14431444
removedIndeterminate = true;
14441445
}
@@ -1479,6 +1480,13 @@ function getSequencePatternInfo(
14791480
let isDefiniteNoMatch = false;
14801481
let isPotentialNoMatch = tupleIndeterminateIndex >= 0 || removedIndeterminate;
14811482

1483+
// If we removed an unbounded entry to make the lengths match,
1484+
// this is a potential match (not definite) because the original
1485+
// tuple could have different lengths.
1486+
if (removedIndeterminate) {
1487+
isPotentialNoMatch = true;
1488+
}
1489+
14821490
// If the pattern includes a "star entry" and the tuple includes an
14831491
// indeterminate-length entry that aligns to the star entry, we can
14841492
// assume it will always match.
@@ -1512,7 +1520,7 @@ function getSequencePatternInfo(
15121520
entryTypes: isDefiniteNoMatch ? [] : typeArgs.map((t) => t.type),
15131521
isIndeterminateLength: false,
15141522
isTuple: true,
1515-
isUnboundedTuple: tupleIndeterminateIndex >= 0,
1523+
isUnboundedTuple: removedIndeterminate || tupleIndeterminateIndex >= 0,
15161524
isDefiniteNoMatch,
15171525
isPotentialNoMatch,
15181526
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This sample tests pattern matching with variadic tuple types.
2+
# From the regression report for Pyright 1.1.408.
3+
4+
from typing import TypeAlias, assert_type
5+
6+
Func6Input: TypeAlias = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]
7+
8+
9+
def func6(val: Func6Input):
10+
match val:
11+
case (x,):
12+
# Type may be narrowed to tuple[int].
13+
# E: Argument of type "tuple[int]" cannot be assigned to parameter of type "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]"
14+
assert_type(val, Func6Input)
15+
assert_type(val, tuple[int])
16+
17+
case (x, y):
18+
# Type may be narrowed to tuple[str, str] | tuple[int, int].
19+
# E: Argument of type "tuple[str, str] | tuple[int, int]" cannot be assigned to parameter of type "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]"
20+
assert_type(val, Func6Input)
21+
assert_type(val, tuple[str, str] | tuple[int, int])
22+
23+
case (x, y, z):
24+
# Type may be narrowed to tuple[int, str, int].
25+
# This case should be reachable!
26+
# E: Argument of type "tuple[int, str, int]" cannot be assigned to parameter of type "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]"
27+
assert_type(val, Func6Input)
28+
assert_type(val, tuple[int, str, int])
29+
30+
case (w, x, y, z):
31+
# Type may be narrowed to tuple[int, str, str, int].
32+
# E: Argument of type "tuple[int, str, str, int]" cannot be assigned to parameter of type "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]"
33+
assert_type(val, Func6Input)
34+
assert_type(val, tuple[int, str, str, int])

packages/pyright-internal/src/tests/typeEvaluator6.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,15 @@ test('MatchSequence2', () => {
471471
TestUtils.validateResults(analysisResults, 0);
472472
});
473473

474+
test('MatchSequenceVariadic', () => {
475+
const configOptions = new ConfigOptions(Uri.empty());
476+
477+
configOptions.defaultPythonVersion = pythonVersion3_12;
478+
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['matchSequenceVariadic.py'], configOptions);
479+
// After fix: should be 4 errors and 0 unreachable code
480+
TestUtils.validateResults(analysisResults, 4, 0, undefined, undefined, 0);
481+
});
482+
474483
test('MatchClass1', () => {
475484
const configOptions = new ConfigOptions(Uri.empty());
476485

0 commit comments

Comments
 (0)