Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,10 @@ junit.xml
Thumbs.db

# Potential venv
.venv
.venv

# Agent files
serena/
.beads/
AGENTS.md

14 changes: 14 additions & 0 deletions packages/pyright-internal/src/analyzer/patternMatching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1423,6 +1423,12 @@ function getSequencePatternInfo(
// If the tuple contains an indeterminate entry, expand or remove that
// entry to match the length of the pattern if possible.
let expandedIndeterminate = false;
// Track when we remove an unbounded entry. When removedIndeterminate is true,
// tupleIndeterminateIndex becomes -1, making isUnboundedTuple false in the
// sequenceInfo entry. This is safe because isPotentialNoMatch=true (set below)
// prevents incorrect elimination at the isDefiniteMatch check before the
// isIndeterminateLength || isUnboundedTuple guard matters.
let removedIndeterminate = false;
if (tupleIndeterminateIndex >= 0) {
tupleDeterminateEntryCount--;

Expand All @@ -1435,6 +1441,7 @@ function getSequencePatternInfo(

if (typeArgs.length > patternEntryCount && patternStarEntryIndex === undefined) {
typeArgs.splice(tupleIndeterminateIndex, 1);
removedIndeterminate = true;
tupleIndeterminateIndex = -1;
}
}
Expand Down Expand Up @@ -1474,6 +1481,13 @@ function getSequencePatternInfo(
let isDefiniteNoMatch = false;
let isPotentialNoMatch = tupleIndeterminateIndex >= 0;

// If we removed an unbounded entry to make the lengths match,
// this is a potential match (not definite) because the original
// tuple could have different lengths.
if (removedIndeterminate) {
isPotentialNoMatch = true;
}

// If the pattern includes a "star entry" and the tuple includes an
// indeterminate-length entry that aligns to the star entry, we can
// assume it will always match.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This sample tests pattern matching with variadic tuple types.
# From the regression report for Pyright 1.1.408.

from typing import TypeAlias, assert_type

Func6Input: TypeAlias = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]


def func6(val: Func6Input):
match val:
case (x,):
# Type may be narrowed to tuple[int].
# E: Argument of type "tuple[int]" cannot be assigned to parameter of type "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]"
assert_type(val, Func6Input)
assert_type(val, tuple[int])

case (x, y):
# Type may be narrowed to tuple[str, str] | tuple[int, int].
# 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]"
assert_type(val, Func6Input)
assert_type(val, tuple[str, str] | tuple[int, int])

case (x, y, z):
# Type may be narrowed to tuple[int, str, int].
# This case should be reachable!
# 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]"
assert_type(val, Func6Input)
assert_type(val, tuple[int, str, int])

case (w, x, y, z):
# Type may be narrowed to tuple[int, str, str, int].
# 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]"
assert_type(val, Func6Input)
assert_type(val, tuple[int, str, str, int])
9 changes: 9 additions & 0 deletions packages/pyright-internal/src/tests/typeEvaluator6.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,15 @@ test('MatchSequence2', () => {
TestUtils.validateResults(analysisResults, 0);
});

test('MatchSequenceVariadic', () => {
const configOptions = new ConfigOptions(Uri.empty());

configOptions.defaultPythonVersion = pythonVersion3_12;
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['matchSequenceVariadic.py'], configOptions);
// After fix: should be 4 errors and 0 unreachable code
TestUtils.validateResults(analysisResults, 4, 0, undefined, undefined, 0);
});

test('MatchClass1', () => {
const configOptions = new ConfigOptions(Uri.empty());

Expand Down
Loading