mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-87890 pandas.DataFrame is considered Any with pandas >= 3 and pycharm 2026.1
(cherry picked from commit 7bab57dfde79fc86f672f7cb14193d7e1cbcdd6a) IJ-MR-193139 GitOrigin-RevId: dc4a5bd0a1f4deb2865525b7db9841ab57ac42dd
This commit is contained in:
committed by
intellij-monorepo-bot
parent
320aed5027
commit
859db02320
+10
-10
@@ -175,17 +175,17 @@ object PyTypeInferenceCspFactory {
|
||||
throw NotSupportedException()
|
||||
}
|
||||
|
||||
for (paramType in generics.typeVars) {
|
||||
if (builder.hasInferenceVariable(paramType)) continue
|
||||
builder.addInferenceVariable(paramType)
|
||||
for (typeParam in generics.typeVars) {
|
||||
if (builder.hasInferenceVariable(typeParam)) continue
|
||||
builder.addInferenceVariable(typeParam)
|
||||
|
||||
// bounds
|
||||
if (paramType.getBound() != null) {
|
||||
val typeVarBound_selfBounded = substituteSelfTypes(paramType.getBound(), receiverType, context)
|
||||
if (typeParam.getBound() != null) {
|
||||
val typeVarBound_selfBounded = substituteSelfTypes(typeParam.getBound(), receiverType, context)
|
||||
// semantics: TV <: Bound
|
||||
builder.addConstraint(paramType, typeVarBound_selfBounded, Variance.COVARIANT, ConstraintPriority.HIGH)
|
||||
builder.addConstraint(typeParam, typeVarBound_selfBounded, Variance.COVARIANT, ConstraintPriority.HIGH)
|
||||
}
|
||||
else if (paramType.getConstraints().isNotEmpty()) {
|
||||
else if (typeParam.getConstraints().isNotEmpty()) {
|
||||
// Note: The Python type variable constraint(s) cannot be fully modeled without a specific CSP constraint that would model a strict logical OR.
|
||||
// A logical OR does unfortunately come with a performance impact since it makes backtracking during the solving process inevitable.
|
||||
// As a solution, Python type variable constraints will be modeled using an approximation that ensures that the type variable is both
|
||||
@@ -194,13 +194,13 @@ object PyTypeInferenceCspFactory {
|
||||
// Only at the very end, during instantiation, an actual set of remaining tv-constraints is chosen.
|
||||
// Note that both of these bounds are necessary to ensure that the TV will be instantiated as exactly one of the given tv-constraints
|
||||
// and not as a subtype of one of the given tv-constraints.
|
||||
val paramTypeConstraints = paramType.getConstraints().map { substituteSelfTypes(it, receiverType, context) }
|
||||
val paramTypeConstraints = typeParam.getConstraints().map { substituteSelfTypes(it, receiverType, context) }
|
||||
val intersectionOfConstraints = PyIntersectionType.intersection(paramTypeConstraints)
|
||||
val unionOfConstraints = PyUnionType.union(paramTypeConstraints)
|
||||
// semantics: TV approximates CV_1 ⊕ CV_2 ⊕ ... ⊕ CV_n by
|
||||
// CV_1 & CV_2 & ... & CV_n <: TV <: CV_1 | CV_2 | ... | CV_n
|
||||
builder.addConstraint(paramType, intersectionOfConstraints, Variance.CONTRAVARIANT, ConstraintPriority.HIGH)
|
||||
builder.addConstraint(paramType, unionOfConstraints, Variance.COVARIANT, ConstraintPriority.HIGH)
|
||||
builder.addConstraint(typeParam, intersectionOfConstraints, Variance.CONTRAVARIANT, ConstraintPriority.HIGH)
|
||||
builder.addConstraint(typeParam, unionOfConstraints, Variance.COVARIANT, ConstraintPriority.HIGH)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
-23
@@ -138,33 +138,42 @@ class CspBuilder(val context: TypeEvalContext) {
|
||||
fun getSolution(keepUnconstrained: Boolean): Solution {
|
||||
val instantiations = if (cp.failed) cp.instantiations else cp.solution
|
||||
val typeVars2TypeRefs: MutableMap<PyTypeVarType, Ref<PyType?>> = LinkedHashMap()
|
||||
for (entry in instantiations) {
|
||||
val instantiatedType = entry.value
|
||||
val instantiatedTypeOrTypeVar: PyType?
|
||||
if (instantiatedType is PyUnconstrainedTypeVariable) {
|
||||
val originalTypeVar = instantiatedType.typeVariable
|
||||
instantiatedTypeOrTypeVar = if (keepUnconstrained) originalTypeVar else originalTypeVar.defaultType?.get()
|
||||
}
|
||||
else if (instantiatedType is PyTypeVarType) {
|
||||
// if the solution is another PyTypeVarType, check the declared default types
|
||||
if (instantiatedType.defaultType?.get() != null) {
|
||||
instantiatedTypeOrTypeVar = instantiatedType.defaultType?.get()
|
||||
}
|
||||
else if (entry.key.typeVariable.defaultType?.get() != null) {
|
||||
instantiatedTypeOrTypeVar = entry.key.typeVariable.defaultType?.get()
|
||||
}
|
||||
else {
|
||||
instantiatedTypeOrTypeVar = instantiatedType
|
||||
}
|
||||
}
|
||||
else {
|
||||
instantiatedTypeOrTypeVar = instantiatedType
|
||||
}
|
||||
typeVars2TypeRefs[entry.key.typeVariable] = Ref.create(instantiatedTypeOrTypeVar)
|
||||
for ((inferenceVariable, instantiatedType) in instantiations) {
|
||||
val instantiatedTypeOrTypeVar = getPostComputedSolution(instantiatedType, inferenceVariable, keepUnconstrained)
|
||||
typeVars2TypeRefs[inferenceVariable.typeVariable] = Ref.create(instantiatedTypeOrTypeVar)
|
||||
}
|
||||
val complete = instantiations.keys.containsAll(cp.inferenceVars.values())
|
||||
return Solution(cp.failed, complete, typeVars2TypeRefs)
|
||||
}
|
||||
|
||||
private fun getPostComputedSolution(
|
||||
instantiatedType: PyType?,
|
||||
inferenceVariable: InferenceVariable,
|
||||
keepUnconstrained: Boolean,
|
||||
): PyType? {
|
||||
when (instantiatedType) {
|
||||
is PyUnconstrainedTypeVariable -> {
|
||||
val originalTypeVar = instantiatedType.typeVariable
|
||||
return when {
|
||||
keepUnconstrained -> originalTypeVar
|
||||
originalTypeVar.defaultType != null -> originalTypeVar.defaultType?.get()
|
||||
originalTypeVar.bound != null -> originalTypeVar.bound
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
is PyTypeVarType -> {
|
||||
// if the solution is another PyTypeVarType, check the declared default types
|
||||
return when {
|
||||
instantiatedType.defaultType?.get() != null -> instantiatedType.defaultType?.get()
|
||||
inferenceVariable.typeVariable.defaultType?.get() != null -> inferenceVariable.typeVariable.defaultType?.get()
|
||||
else -> instantiatedType
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
return instantiatedType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ConstraintPriority {
|
||||
@@ -1371,6 +1380,12 @@ private object TypeBoundResolver {
|
||||
return PyUnionType.union(lowerBoundsWidened)
|
||||
}
|
||||
else if (lowerBounds.isEmpty() && upperBounds.isNotEmpty()) {
|
||||
if (upperBounds.size == 1 && upperBounds[0] == infVar.typeVariable.bound) {
|
||||
// special case: the type variable is constrained only by its bound (i.e., `[T : int]`).
|
||||
// Therefore, we treat this type variable as unconstrained and use its bound when necessary based on `#keepUnconstrained`.
|
||||
return PyUnconstrainedTypeVariable(infVar.typeVariable)
|
||||
}
|
||||
|
||||
// It is debatable whether we should just return PyIntersectionType.intersection(*upperBounds)
|
||||
// Note however that intersection types are not part of Python (as of 2026).
|
||||
// Hence, the following logic makes it mandatory that the user declares a common subtype at some point.
|
||||
|
||||
@@ -386,7 +386,7 @@ class PyTypeInferenceCspTest : PyInspectionTestCase() {
|
||||
|
||||
fun `test Handle inferred intersections 2`() {
|
||||
doTestByText("""
|
||||
from typing import Callable, TypeVar, assert_type, Never
|
||||
from typing import Callable, TypeVar, assert_type, Never, Any
|
||||
|
||||
class A: ...
|
||||
class B: ...
|
||||
@@ -437,7 +437,6 @@ class PyTypeInferenceCspTest : PyInspectionTestCase() {
|
||||
""")
|
||||
}
|
||||
|
||||
|
||||
@TestFor(issues = ["PY-86098"])
|
||||
fun `test PY-86098`() {
|
||||
doTestByText("""
|
||||
@@ -448,4 +447,70 @@ class PyTypeInferenceCspTest : PyInspectionTestCase() {
|
||||
assert_type(a1, A[int])
|
||||
""")
|
||||
}
|
||||
|
||||
@TestFor(issues = ["PY-87890"])
|
||||
fun `test Nested csp with type parameter bound`() {
|
||||
doTestByText("""
|
||||
from typing import Any, Callable, assert_type
|
||||
|
||||
def f[F: Callable[..., Any]]() -> Callable[[F], F]:
|
||||
return lambda x: x
|
||||
|
||||
assert_type(f()(lambda x: 1)(1), int)
|
||||
""")
|
||||
}
|
||||
|
||||
@TestFor(issues = ["PY-87890"])
|
||||
fun `test Nested csp with type parameter default`() {
|
||||
doTestByText("""
|
||||
from typing import Callable, assert_type, Optional
|
||||
|
||||
def f[T = str]() -> Callable[[Optional[T]], T]: ...
|
||||
|
||||
assert_type(f()(2), int)
|
||||
assert_type(f()(), str)
|
||||
""")
|
||||
}
|
||||
|
||||
fun `test Nested csp with type parameter default Any`() {
|
||||
doTestByText("""
|
||||
from typing import Callable, assert_type, Any, Optional
|
||||
|
||||
def f[T = Any]() -> Callable[[Optional[T]], T]: ...
|
||||
|
||||
assert_type(f()(2), int)
|
||||
assert_type(f()(), Any)
|
||||
""")
|
||||
}
|
||||
|
||||
fun `test Nested csp with type parameter constraint`() {
|
||||
fixme("Support for combined CSPs necessary", AssertionError::class.java) {
|
||||
doTestByText("""
|
||||
from typing import Callable, assert_type, Any
|
||||
|
||||
def f[T : (str, int)]() -> Callable[[T], T]: ...
|
||||
|
||||
assert_type(f()(2), int)
|
||||
assert_type(f()("s"), str)
|
||||
""")
|
||||
}
|
||||
}
|
||||
|
||||
fun `test Keep unconstrained type parameters for type return`() {
|
||||
doTestByText("""
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
T = TypeVar("T", infer_variance=False)
|
||||
|
||||
class Box(Generic[T]):
|
||||
...
|
||||
|
||||
def box_class() -> type[Box[T]]:
|
||||
return Box
|
||||
|
||||
C = box_class()
|
||||
box_int : Box[int] = C()
|
||||
assert_type(box_int, Box[int])
|
||||
""")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user