From f977a01dfab9b26809b000e410bc2427807da6ec Mon Sep 17 00:00:00 2001 From: Daniil Kalinin Date: Fri, 11 Apr 2025 16:25:47 +0200 Subject: [PATCH] PY-76865 Fix Conformance test failure: generics_typevartuple_args.py Fix the situations when two tuples passed as function arguments were not matched correctly GitOrigin-RevId: fae271e16ed0933343e9e00a77f9f316c6f99798 --- .../python/inspections/PyTypeCheckerInspection.java | 12 +++++++----- .../testData/typing/conformance/tests/_ignored.txt | 1 - .../inspections/Py3TypeCheckerInspectionTest.java | 8 +++----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java index 2d2e61438948..94c2abbb36e9 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java +++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java @@ -542,15 +542,17 @@ public class PyTypeCheckerInspection extends PyInspection { // For an expected type with generics we have to match all the actual types against it in order to do proper generic unification if (PyTypeChecker.hasGenerics(expected, myTypeEvalContext)) { - // First collect type parameter substitutions by matching the expected type with the union. - PyType actualJoin = PyUnionType.union(ContainerUtil.map(arguments, myTypeEvalContext::getType)); - matchParameterAndArgument(expected, actualJoin, null, substitutions); - PyType expectedWithSubstitutions = substituteGenerics(expected, substitutions); + // First collect type parameter substitutions by matching the expected type with the union, if it's a keyword container + // otherwise, match as usual arguments, passed to a function + if (container.isKeywordContainer()) { + PyType actualJoin = PyUnionType.union(ContainerUtil.map(arguments, myTypeEvalContext::getType)); + matchParameterAndArgument(expected, actualJoin, null, substitutions); + } return ContainerUtil.map(arguments, argument -> { // Then match each argument type against the expected type after these substitutions. PyType actual = myTypeEvalContext.getType(argument); boolean matched = matchParameterAndArgument(expected, actual, argument, substitutions); - return new AnalyzeArgumentResult(argument, expected, expectedWithSubstitutions, actual, matched); + return new AnalyzeArgumentResult(argument, expected, substituteGenerics(expected, substitutions), actual, matched); }); } else { diff --git a/python/testData/typing/conformance/tests/_ignored.txt b/python/testData/typing/conformance/tests/_ignored.txt index 7b5dca2bd0c4..ea150f804aeb 100644 --- a/python/testData/typing/conformance/tests/_ignored.txt +++ b/python/testData/typing/conformance/tests/_ignored.txt @@ -37,7 +37,6 @@ generics_self_usage.py generics_syntax_declarations.py generics_syntax_infer_variance.py generics_syntax_scoping.py -generics_typevartuple_args.py generics_typevartuple_basic.py generics_typevartuple_overloads.py generics_typevartuple_specialization.py diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index aa28c5c448d6..651fc92cb035 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -1916,21 +1916,19 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { """); } - // PY-53105 + // PY-53105, PY-76865 public void testVariadicGenericStarArgsOfVariadicGeneric() { doTestByText(""" from typing import Tuple, TypeVarTuple Ts = TypeVarTuple('Ts') - def foo(*args: Tuple[*Ts]): ... - foo((0,), (1,)) foo((0,), (1, 2)) - # *tuple[int | str] is inferred for *Ts - foo((0,), ('1',)) + # Should fail according to https://typing.python.org/en/latest/spec/generics.html#type-variable-tuple-equality + foo((0,), ('1',)) """); }