diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVisitor.kt b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVisitor.kt index c69256c673af..5aa323992e4b 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVisitor.kt +++ b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeVisitor.kt @@ -1,27 +1,27 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.jetbrains.python.psi.types import org.jetbrains.annotations.ApiStatus /** * Similarly to [com.intellij.psi.PsiElementVisitor], implements double dispatching for the [PyType] hierarchy. - * - * + * + * * Because the "unknown" type is historically represented as `null` in the type system, `PyTypeVisitor.visitPyType(type, visitor)` * should be used instead of direct `type.acceptTypeVisitor(visitor)` to properly account for possible `null` values. - * - * + * + * * This class gives access only to the types declared in the intellij.python.psi module. * Most actual implementations should extend [PyTypeVisitorExt]. - * - * + * + * * There are helper [PyRecursiveTypeVisitor] and [PyCloningTypeVisitor] for recursive type * traversal and deep cloning of a type respectively. - * + * * @see PyRecursiveTypeVisitor - * + * * @see PyCloningTypeVisitor - * + * * @see PyType.acceptTypeVisitor * @see .visit * @see .visitUnknownType @@ -74,11 +74,11 @@ abstract class PyTypeVisitor { } open fun visitAnyType(): T? { - return null + return if (PyAnyType.isEnabled) visitPyType(PyAnyType.Any) else null } open fun visitUnknownType(): T? { - return null + return if (PyAnyType.isEnabled) visitPyType(PyAnyType.Unknown) else null } companion object { diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java index d237bd020bbb..6a4b7519d477 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java @@ -21,6 +21,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.jetbrains.python.psi.PyElement; import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.types.PyAnyType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NonNls; @@ -31,7 +32,7 @@ public final class ReadWriteInstruction extends InstructionImpl { private static InstructionTypeCallback instructionTypeCallback(@Nullable PsiElement element) { return element instanceof PyExpression expression ? context -> Ref.create(context.getType(expression)) - : context -> Ref.create(null); + : context -> Ref.create(PyAnyType.getUnknown()); } public enum ACCESS { diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typeRepresentation/psi/PyFunctionTypeRepresentation.kt b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typeRepresentation/psi/PyFunctionTypeRepresentation.kt index 5881d2340dfb..0ad3d5a1e1f0 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typeRepresentation/psi/PyFunctionTypeRepresentation.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typeRepresentation/psi/PyFunctionTypeRepresentation.kt @@ -36,6 +36,7 @@ import com.jetbrains.python.psi.types.PyCallableTypeImpl import com.jetbrains.python.psi.types.PyCollectionTypeImpl import com.jetbrains.python.psi.types.PyFunctionTypeImpl import com.jetbrains.python.psi.types.PyType +import com.jetbrains.python.psi.types.PyTypeUtil.derefOrUnknown import com.jetbrains.python.psi.types.PyTypeVarType import com.jetbrains.python.psi.types.PyTypeVarTypeImpl import com.jetbrains.python.psi.types.TypeEvalContext @@ -209,7 +210,7 @@ class PyFunctionTypeRepresentation(astNode: ASTNode) : PyElementImpl(astNode), P // Otherwise, resolve normally return when (expr) { is PyDoubleStarExpression -> PyTypingTypeProvider.getType(expr.expression!!, context)?.get() - else -> PyTypingTypeProvider.getType(expr, context)?.get() + else -> PyTypingTypeProvider.getType(expr, context).derefOrUnknown() } } } diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.kt b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.kt index 9e390161a89b..23fbbc35cb66 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.kt @@ -122,6 +122,7 @@ import com.jetbrains.python.psi.types.PyTypeParameterType import com.jetbrains.python.psi.types.PyTypeParser import com.jetbrains.python.psi.types.PyTypeUtil import com.jetbrains.python.psi.types.PyTypeUtil.convertToType +import com.jetbrains.python.psi.types.PyTypeUtil.derefOrUnknown import com.jetbrains.python.psi.types.PyTypeUtil.toKeywordContainerType import com.jetbrains.python.psi.types.PyTypeUtil.toPositionalContainerType import com.jetbrains.python.psi.types.PyTypeVarTupleType @@ -179,7 +180,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { if (annotation != null) { val funcTypeCommentParamHint: PyExpression? = findParamTypeHintInFunctionTypeComment(annotation, param, func) if (funcTypeCommentParamHint == null) { - return Ref() + return Ref(PyAnyType.unknown) } typeHint = funcTypeCommentParamHint } @@ -199,7 +200,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { ) { typeHint = typeHint.qualifier!! } - val type = Ref.deref(getType(typeHint, context)) + val type = getType(typeHint, context).derefOrUnknown() if (param.isPositionalContainer && type !is PyParamSpecType) { return Ref(param.toPositionalContainerType(type)) } @@ -225,7 +226,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { return typeRef } // Don't rely on other type providers if a type hint is present but cannot be resolved. - return Ref() + return Ref(PyAnyType.unknown) } } return null @@ -316,7 +317,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { } } - val annotatedType: Ref? = getTypeFromTypeHint(referenceTarget, context) + val annotatedType = getTypeFromTypeHint(referenceTarget, context) if (annotatedType != null) { return annotatedType } @@ -991,8 +992,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { .collect(PyTypeUtil.toUnionFromRef()) } - private fun - getTypeFromTypeHint(element: T, context: Context): Ref? where T : PyAnnotationOwner?, T : PyTypeCommentOwner? { + private fun getTypeFromTypeHint(element: T, context: Context): Ref? where T : PyAnnotationOwner?, T : PyTypeCommentOwner? { val annotation: PyExpression? = getAnnotationValue(element!!, context.typeContext) if (annotation != null) { return getType(annotation, context) @@ -1034,7 +1034,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { private fun evaluateSuperClassesAsTypeHints(pyClass: PyClass, context: TypeEvalContext): MutableList { val results: MutableList = ArrayList() for (superClassExpression in getSuperClassExpressions(pyClass)) { - val type = Ref.deref(getType(superClassExpression, context)) + val type = getType(superClassExpression, context).derefOrUnknown() if (type is PyClassType) { results.add(type) } @@ -1394,7 +1394,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { if (typeEngineType != null) { return typeEngineType } - return PyAnyType.unknown?.let { Ref(it) } + return null } finally { if (resolved is PyClass) { @@ -1409,7 +1409,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { private fun getTypeEngineType(typeHint: PyExpression, context: Context): Ref? { if (!context.typeRepresentationMode) return null if (typeHint.text == "Unknown") { - return Ref() + return Ref(PyAnyType.unknown) } if (typeHint is PyFunctionTypeRepresentation) { val result = context.typeContext.getType(typeHint) @@ -1554,7 +1554,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { if (resolved is PyQualifiedNameOwner) { val qualifiedName = resolved.qualifiedName if (TYPE_ALIAS == qualifiedName || TYPE_ALIAS_EXT == qualifiedName) { - return Ref() + return Ref(PyAnyType.unknown) } } return null @@ -1592,7 +1592,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { return getAsClassObjectType(indexExpr, context) } // Map Type[Something] with unsupported type parameter to Any, instead of a generic type for the class "type" - return Ref() + return Ref(PyAnyType.unknown) } } else if (TYPE == getQualifiedName(resolved)) { @@ -1621,7 +1621,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { ) { return Ref(type.map { (it as PyClassType).toClass() }) } - return Ref() + return Ref(PyAnyType.unknown) } private fun getAnyType(element: PsiElement, context: Context): Ref? { @@ -1695,7 +1695,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { return Ref(PyUnionType.union(typeRef.get(), getInstance(element).noneType)) } } - return Ref() + return Ref(PyAnyType.unknown) } } return null @@ -2121,7 +2121,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { if (arguments.size < 2) return null val prefixTypeExprs = arguments.subList(0, arguments.size - 1) - val prefixTypes = prefixTypeExprs.map { Ref.deref(getType(it!!, context.typeContext)) } + val prefixTypes = prefixTypeExprs.map { getType(it, context.typeContext).derefOrUnknown() } val lastTypeExpr = arguments[arguments.size - 1] val paramSpecType = if (lastTypeExpr is PyEllipsisLiteralExpression) { @@ -2161,7 +2161,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { .takeWhile { it !is PyKeywordArgument } .map { Ref.deref(getType(it, context)) } val boundExpression = element.getKeywordArgument("bound") - val bound = if (boundExpression == null) null else Ref.deref(getType(boundExpression, context)) + val bound = if (boundExpression == null) PyAnyType.unknown else Ref.deref(getType(boundExpression, context)) val variance: PyTypeVarType.Variance = getTypeVarVarianceFromDeclaration(element) val assignStmt = element.getParent() as? PyAssignmentStatement val mappingPair = assignStmt?.targetsToValuesMapping?.firstOrNull { pair -> pair.second == element } @@ -2253,7 +2253,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext() { defaultType = if (defaultExprWithoutParens != null) getTypePreventingRecursion(defaultExprWithoutParens, context) else - Ref() + Ref(PyAnyType.unknown) } val declarationElement = element as? PyQualifiedNameOwner diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java index 64cec3a88be8..f0cdcb0abe9a 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyBinaryExpressionImpl.java @@ -12,6 +12,7 @@ import com.jetbrains.python.psi.PyElementVisitor; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.impl.references.PyOperatorReference; import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.types.PyAnyType; import com.jetbrains.python.psi.types.PyStructuralType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.PyTypeChecker; @@ -21,6 +22,8 @@ import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static com.jetbrains.python.psi.types.PyTypeUtilKt.isUnknown; + public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExpression { @@ -66,7 +69,7 @@ public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExp final PyExpression right = getRightExpression(); final PyType rightType = right != null ? context.getType(right) : null; if (leftType == null && rightType == null) { - return null; + return PyAnyType.getUnknown(); } if (isOperator(PyNames.OR)) { // TODO: also exclude Literal[False, 0, ""] @@ -80,13 +83,13 @@ public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExp return PyBuiltinCache.getInstance(this).getBoolType(); } PyType callResultType = PyCallExpressionHelper.getCallType(this, context, key); - if (callResultType == null) { + if (callResultType instanceof PyAnyType.Any) return callResultType; + if (isUnknown(callResultType)) { if (referencedName != null && PyNames.COMPARISON_OPERATORS.contains(referencedName)) { - // we don't know if it was explicit or not, so we form an unsafe union of Any and bool - // TODO: when { explicit Any -> Any, Unknown -> UnsafeUnion[bool | Any] } - return PyUnsafeUnionType.unsafeUnion(null, PyBuiltinCache.getInstance(this).getBoolType()); + // it was not an explicit `Any`, so we form an unsafe union of `Unknown` and `bool` + return PyUnsafeUnionType.unsafeUnion(callResultType, PyBuiltinCache.getInstance(this).getBoolType()); } - return null; + return callResultType; } boolean bothOperandsAreKnown = operandIsKnown(getLeftExpression(), context) && operandIsKnown(getRightExpression(), context); // TODO requires weak union. See PyTypeCheckerInspectionTest#testBinaryExpressionWithUnknownOperand diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.kt index f71376782dae..c4ccda35c852 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.kt @@ -52,6 +52,7 @@ import com.jetbrains.python.psi.resolve.PyResolveUtil import com.jetbrains.python.psi.resolve.QualifiedRatedResolveResult import com.jetbrains.python.psi.resolve.QualifiedResolveResult import com.jetbrains.python.psi.resolve.RatedResolveResult +import com.jetbrains.python.psi.types.PyAnyType import com.jetbrains.python.psi.types.PyCallableParameter import com.jetbrains.python.psi.types.PyCallableParameterImpl import com.jetbrains.python.psi.types.PyCallableType @@ -78,6 +79,7 @@ import com.jetbrains.python.psi.types.PyUnionType import com.jetbrains.python.psi.types.PyUnsafeUnionType import com.jetbrains.python.psi.types.TypeEvalContext import com.jetbrains.python.psi.types.isNoneType +import com.jetbrains.python.psi.types.isUnknown import com.jetbrains.python.pyi.PyiUtil import com.jetbrains.python.toolbox.Maybe import org.jetbrains.annotations.ApiStatus @@ -231,7 +233,7 @@ private fun PyCallExpression.getExplicitResolveResults(resolveContext: PyResolve for (type in calleeType.toStream()) { // When invoking cls(), turn type[Self] into Self. // Otherwise, we will delegate to __init__() of its scope class and return a concrete type class - // as a call result, losing Self. + // as a call result, losing Self. // See e.g. Py3TypeCheckerInspectionTest.testSelfInClassMethods if (type is PySelfType) { result.add(type) @@ -365,9 +367,9 @@ private fun PyCallSiteExpression.toCallableType( clarifiedResolved.getImplicitArgumentCount(resolvedModifier, isConstructorCall, isByInstance, isByClass) val clarifiedConstructorCallType = - if (PyUtil.isInitOrNewMethod(clarifiedResolved)) resolveResult.clarifyConstructorCallType(this, context) else null + if (PyUtil.isInitOrNewMethod(clarifiedResolved)) resolveResult.clarifyConstructorCallType(this, context) else PyAnyType.unknown - if (callableType.modifier == resolvedModifier && callableType.implicitOffset == resolvedImplicitOffset && clarifiedConstructorCallType == null) { + if (callableType.modifier == resolvedModifier && callableType.implicitOffset == resolvedImplicitOffset && clarifiedConstructorCallType.isUnknown) { return callableType } @@ -601,14 +603,14 @@ private fun List.resolveOverloadsCallType(callSite: PyCallSiteEx return matchingOverloads[0].getCallType(context, callSite) } val someArgumentsHaveUnknownType = arguments.any { - context.getType(it) == null + context.getType(it).isUnknown } if (someArgumentsHaveUnknownType) { return matchingOverloads .map { it.getCallType(context, callSite) } .let { PyUnionType.union(it) } } - return matchingOverloads.firstOrNull()?.getCallType(context, callSite) + return matchingOverloads.firstOrNull()?.getCallType(context, callSite) ?: PyAnyType.unknown } private fun ClarifiedResolveResult.clarifyConstructorCallType(callSite: PyCallSiteExpression, context: TypeEvalContext): PyType? { @@ -634,12 +636,12 @@ private fun ClarifiedResolveResult.clarifyConstructorCallType(callSite: PyCallSi if (initOrNewCallType is PyCollectionType) { return initOrNewCallType } - if (initOrNewCallType == null) { + if (initOrNewCallType.isUnknown) { // TODO requires weak union. See PyUnresolvedReferencesInspectionTest.testCustomNewReturnInAnotherModule return PyUnionType.createWeakType(PyClassTypeImpl(receiverClass, false)) } - return null + return PyAnyType.unknown } private fun PyCallExpression.getSuperCallType(context: TypeEvalContext): Maybe { diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 894ef579d507..2231e4386eea 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -91,6 +91,7 @@ import com.jetbrains.python.psi.stubs.PropertyStubStorage; import com.jetbrains.python.psi.stubs.PyClassStub; import com.jetbrains.python.psi.stubs.PyFunctionStub; import com.jetbrains.python.psi.stubs.PyTargetExpressionStub; +import com.jetbrains.python.psi.types.PyAnyType; import com.jetbrains.python.psi.types.PyCallableParameter; import com.jetbrains.python.psi.types.PyClassLikeType; import com.jetbrains.python.psi.types.PyClassType; @@ -148,7 +149,7 @@ public class PyClassImpl extends PyBaseElementImpl implements PyCla @Override public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { if (PyTypingTypeProvider.ANY.equals(getQualifiedName())) { - return null; + return PyAnyType.getAny(); } return new PyClassTypeImpl(this, true); } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 05dab8409e8e..e5185a04649a 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -72,6 +72,7 @@ import com.jetbrains.python.psi.stubs.PyAnnotationOwnerStub; import com.jetbrains.python.psi.stubs.PyClassStub; import com.jetbrains.python.psi.stubs.PyFunctionStub; import com.jetbrains.python.psi.stubs.PyTargetExpressionStub; +import com.jetbrains.python.psi.types.PyAnyType; import com.jetbrains.python.psi.types.PyCallableParameter; import com.jetbrains.python.psi.types.PyCallableParameterImpl; import com.jetbrains.python.psi.types.PyCallableType; @@ -110,6 +111,7 @@ import static com.jetbrains.python.ast.PyAstFunction.Modifier.STATICMETHOD; import static com.jetbrains.python.psi.PyUtil.as; import static com.jetbrains.python.psi.impl.PyCallExpressionHelper.interpretAsModifierWrappingCall; import static com.jetbrains.python.psi.impl.PyDeprecationUtilKt.extractDeprecationMessageFromDecorator; +import static com.jetbrains.python.psi.types.PyTypeUtilKt.isUnknown; public class PyFunctionImpl extends PyBaseElementImpl implements PyFunction { @@ -221,7 +223,7 @@ public class PyFunctionImpl extends PyBaseElementImpl implements @Override public @Nullable PyType getInferredReturnType(@NotNull TypeEvalContext context) { - PyType inferredType = null; + PyType inferredType = PyAnyType.getUnknown(); if (context.allowReturnTypes(this)) { final PyType returnType = getReturnStatementType(context); final Pair yieldSendTypePair = getYieldExpressionType(context); @@ -307,14 +309,14 @@ public class PyFunctionImpl extends PyBaseElementImpl implements type = PyTypeChecker.substitute(type, substitutionsWithUnresolvedReturnGenerics, context); } else { - type = null; + type = PyAnyType.getUnknown(); } } // TODO Is it still needed if we infer Self as a return type? else if (receiver != null) { type = replaceSelf(type, receiver, context); } - if (type != null && isDynamicallyEvaluated(parameters.values(), context)) { + if (!isUnknown(type) && isDynamicallyEvaluated(parameters.values(), context)) { type = PyUnionType.createWeakType(type); } return PyNarrowedType.Companion.bindIfNeeded(type, callSiteExpression); diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java index 7ef1915b2673..e71e90dbbd9c 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java @@ -11,6 +11,7 @@ import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyLambdaExpression; import com.jetbrains.python.psi.PyNamedParameter; import com.jetbrains.python.psi.PySlashParameter; +import com.jetbrains.python.psi.types.PyAnyType; import com.jetbrains.python.psi.types.PyCallableParameter; import com.jetbrains.python.psi.types.PyCallableParameterImpl; import com.jetbrains.python.psi.types.PyCallableType; @@ -104,7 +105,7 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp @Override public @Nullable PyType getReturnType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { final PyExpression body = getBody(); - if (body == null) return null; + if (body == null) return PyAnyType.getUnknown(); final PyFunctionImpl.YieldCollector visitor = new PyFunctionImpl.YieldCollector(); body.accept(visitor); diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java index 99bc8b4695ea..07ab7bab531c 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java @@ -49,6 +49,7 @@ import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.stubs.PyAnnotationOwnerStub; import com.jetbrains.python.psi.stubs.PyNamedParameterStub; +import com.jetbrains.python.psi.types.PyAnyType; import com.jetbrains.python.psi.types.PyCallableParameter; import com.jetbrains.python.psi.types.PyCallableParameterImpl; import com.jetbrains.python.psi.types.PyClassTypeImpl; @@ -299,7 +300,7 @@ public class PyNamedParameterImpl extends PyBaseElementImpl typeFromTargetsRef = getTypeFromTargets(context); - final PyType typeFromTargets = Ref.deref(typeFromTargetsRef); + final PyType typeFromTargets = PyTypeUtil.derefOrUnknown(typeFromTargetsRef); if (qualified && isNoneType(typeFromTargets)) { - return null; + return PyAnyType.getUnknown(); } final Ref descriptorType = PyDescriptorTypeUtil.getDunderGetReturnType(this, typeFromTargets, context); @@ -264,7 +266,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere return getTypeFromDunderGetAttr(context); } - return Ref.deref(typeFromTargetsRef); + return typeFromTargets; } private @Nullable PyType getCallableType(@NotNull TypeEvalContext context) { @@ -422,7 +424,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere LOG.info(PluginException.createByClass("Failed to get expression type via " + provider.getClass(), e, provider.getClass())); } } - return null; + return PyAnyType.getUnknown(); } private static @Nullable Ref getTypeFromTarget(@NotNull PsiElement target, @@ -485,7 +487,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere if (!ScopeUtil.getElementsOfAccessType(name, scopeOwner, ReadWriteInstruction.ACCESS.ASSERTTYPE).isEmpty() || (target instanceof PyTargetExpression || target instanceof PyNamedParameter) && ScopeUtil.getScopeOwner(target) == scopeOwner) { final PyType type = getTypeByControlFlow(name, context, anchor, scopeOwner); - if (type != null) { + if (!isUnknown(type)) { return Ref.create(type); } } @@ -572,7 +574,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere if (narrowedType.getTypeIs()) { return PyTypeAssertionEvaluator.createAssertionType(initial, type, positive, false, context); } - return Ref.create((positive) ? type : initial); + return Ref.create(positive ? type : initial); } } } @@ -581,7 +583,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere }) .nonNull() .collect(PyTypeUtil.toUnionFromRef()); - return Ref.deref(combinedType); + return PyTypeUtil.derefOrUnknown(combinedType); } public static @Nullable Ref getReferenceTypeFromProviders(@NotNull PsiElement target, diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java index 8b077149af49..062d6c478857 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java @@ -73,6 +73,7 @@ import com.jetbrains.python.psi.stubs.PyFunctionStub; import com.jetbrains.python.psi.stubs.PyLiteralKind; import com.jetbrains.python.psi.stubs.PyTargetExpressionStub; import com.jetbrains.python.psi.types.PyABCUtil; +import com.jetbrains.python.psi.types.PyAnyType; import com.jetbrains.python.psi.types.PyCallableParameter; import com.jetbrains.python.psi.types.PyClassLikeType; import com.jetbrains.python.psi.types.PyClassType; @@ -97,6 +98,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import static com.jetbrains.python.psi.types.PyTypeUtilKt.isUnknown; + public class PyTargetExpressionImpl extends PyBaseElementImpl implements PyTargetExpression { private volatile @Nullable QualifiedName myQualifiedName; @@ -147,14 +150,14 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl pyType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(this, context, null); if (pyType != null) { return pyType.get(); } PyType type = getTypeFromDocString(); - if (type != null) { + if (!isUnknown(type)) { return type; } if (!context.maySwitchToAST(this)) { @@ -188,7 +191,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl getIterationType(member, source, anchor, isAsync, context)); } if (!isAsync) { - if (iterableType != null && PyABCUtil.isSubtype(iterableType, PyNames.ITERABLE, context)) { + if (!isUnknown(iterableType) && PyABCUtil.isSubtype(iterableType, PyNames.ITERABLE, context)) { final PyFunction iterateMethod = findMethodByName(iterableType, PyNames.ITER, context); if (iterateMethod != null) { final PyType iterateReturnType = getContextSensitiveType(iterateMethod, context, source); @@ -472,7 +475,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl getNextMethodCallType(@Nullable PyType type, @@ -572,7 +575,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl { promoteDictLiteral(expectedType, value) @@ -121,7 +121,7 @@ class PyLiteralType private constructor(cls: PyClass, val expression: PyExpressi } else -> { val type = if (inferLiteralTypes) getLiteralOrLiteralStringType(value, context) else null - return type ?: context.getType(value) + type ?: context.getType(value) } } } @@ -202,7 +202,7 @@ class PyLiteralType private constructor(cls: PyClass, val expression: PyExpressi ): PyType? { val substitution = if (substitutions != null) PyTypeChecker.substitute(expected, substitutions, context) else expected val substitutionOrBound = if (substitution is PyTypeVarType) substitution.getEffectiveBound() else substitution - if (substitutionOrBound == null) return null + if (substitutionOrBound == null) return PyAnyType.unknown return TypePromoter(context, containsLiteral(substitutionOrBound)).promoteToType(substitutionOrBound, expression) } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PySyntheticCallHelper.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PySyntheticCallHelper.java index 485d71d6fd1b..df53ae7419a6 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PySyntheticCallHelper.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PySyntheticCallHelper.java @@ -60,7 +60,7 @@ public final class PySyntheticCallHelper { @NotNull List argumentTypes, @NotNull TypeEvalContext context) { List functions = resolveFunctionsByArgumentTypes(functionName, argumentTypes, receiverType, context); - if (functions.isEmpty()) return null; + if (functions.isEmpty()) return PyAnyType.getUnknown(); return StreamEx.of(functions) .nonNull() .map(function -> getCallTypeOnTypesOnly(function, receiverType, argumentTypes, context)) diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.kt index 7fc92e65f745..2bd4ea2735a5 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeChecker.kt @@ -45,6 +45,7 @@ import com.jetbrains.python.psi.types.PyLiteralStringType.Companion.match import com.jetbrains.python.psi.types.PyLiteralType.Companion.match import com.jetbrains.python.psi.types.PyRecursiveTypeVisitor.PyTypeTraverser import com.jetbrains.python.psi.types.PyTypeChecker.match +import com.jetbrains.python.psi.types.PyTypeUtil.derefOrUnknown import com.jetbrains.python.psi.types.PyTypeUtil.getEffectiveBound import com.jetbrains.python.psi.types.PyTypeUtil.toStream import com.jetbrains.python.pyi.PyiFile @@ -177,7 +178,7 @@ object PyTypeChecker { return Optional.of(match(expected, actual, context)) } - if (expected == null || actual == null || isUnknown(actual, context.context)) { + if (expected.isAnyOrUnknown || actual.isAnyOrUnknown || isUnknown(actual, context.context)) { return Optional.of(true) } @@ -358,13 +359,13 @@ object PyTypeChecker { } } - if (safeActual != null) { + if (!safeActual.isUnknown) { val type = if (constraints.isEmpty()) safeActual else constraints[matchedConstraintIndex] context.mySubstitutions.putTypeVar(expected, Ref(type), KeyImpl) } else { val effectiveBound = expected.getEffectiveBound() - if (effectiveBound != null) { + if (!effectiveBound.isUnknown) { context.mySubstitutions.putTypeVar(expected, Ref(PyUnionType.createWeakType(effectiveBound)), KeyImpl) } } @@ -1392,7 +1393,7 @@ object PyTypeChecker { return typeVarType } val substitutionRef = substitutions.typeVars[typeVarType] - var substitution = Ref.deref(substitutionRef) + var substitution = substitutionRef.derefOrUnknown() if (substitutionRef == null) { val invertedTypeVar: PyInstantiableType<*> = typeVarType.invert() val invertedSubstitution = Ref.deref(substitutions.typeVars[invertedTypeVar]) as? PyInstantiableType<*> @@ -1762,7 +1763,7 @@ object PyTypeChecker { } receiverType.toStream() .select(PyClassType::class.java) - .map { type: PyClassType? -> collectTypeSubstitutions(type!!, context) } + .map { collectTypeSubstitutions(it, context) } .forEach { newSubstitutions -> for (typeVarMapping in newSubstitutions.typeVars.entries) { substitutions.putTypeVar(typeVarMapping.key, typeVarMapping.value, KeyImpl, true) diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeInferenceCspFactory.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeInferenceCspFactory.kt index e8ab47ad0271..d903b7664dc7 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeInferenceCspFactory.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeInferenceCspFactory.kt @@ -75,10 +75,10 @@ object PyTypeInferenceCspFactory { val builder = CspBuilder(context) - for (typeVarEntry in substitutions.typeVars.entries) { - ensureInferenceVariables(builder, receiverType, typeVarEntry.key, context) - if (typeVarEntry.value != null) { - builder.addConstraint(typeVarEntry.key, typeVarEntry.value!!.get(), Variance.INVARIANT, ConstraintPriority.HIGH) + for ((key, value) in substitutions.typeVars) { + ensureInferenceVariables(builder, receiverType, key, context) + if (value != null) { + builder.addConstraint(key, value.get(), Variance.INVARIANT, ConstraintPriority.HIGH) } } @@ -89,9 +89,7 @@ object PyTypeInferenceCspFactory { } // arguments - for (entry in mappedParameters) { - val argument = entry.key - val parameter: PyCallableParameter = entry.value + for ((argument, parameter) in mappedParameters) { if (parameter.isPositionalContainer() || parameter.isKeywordContainer()) { throw NotSupportedException() } @@ -99,7 +97,7 @@ object PyTypeInferenceCspFactory { val expectedParameterType = parameter.getArgumentType(context) val passedArgumentType = getArgumentType(parameter, argument, expectedParameterType, substitutions, context) - if (expectedParameterType != null + if (!expectedParameterType.isUnknown && (expectedParameterType.hasGenerics(context) || passedArgumentType.hasGenerics(context)) ) { ensureInferenceVariables(builder, receiverType, expectedParameterType, context) @@ -113,7 +111,7 @@ object PyTypeInferenceCspFactory { if (declaredReturn.hasGenerics(context)) { ensureInferenceVariables(builder, receiverType, declaredReturn, context) val expectedReturnType = getExpectedType(callSite, context) - if (expectedReturnType != null) { + if (!expectedReturnType.isUnknown) { val declaredReturn_selfBounded = substituteSelfTypes(declaredReturn, receiverType, context) // semantics: RT <: ExpectedReturnType builder.addConstraint(declaredReturn_selfBounded, expectedReturnType, Variance.COVARIANT, ConstraintPriority.LOW) diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeInferenceCspSolver.kt b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeInferenceCspSolver.kt index 1b72ccbcd202..1c83d21915fb 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeInferenceCspSolver.kt +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyTypeInferenceCspSolver.kt @@ -24,6 +24,7 @@ import com.jetbrains.python.psi.resolve.PyResolveContext import com.jetbrains.python.psi.resolve.RatedResolveResult import com.jetbrains.python.psi.types.ConstraintReducer.reduce import com.jetbrains.python.psi.types.PyRecursiveTypeVisitor.PyTypeTraverser +import com.jetbrains.python.psi.types.PyTypeUtil.derefOrUnknown import com.jetbrains.python.psi.types.PyTypeUtil.getEffectiveBound import com.jetbrains.python.psi.types.PyTypeVarType.Variance import com.jetbrains.python.psi.types.SubtypeJudgement.isRawSubtype @@ -156,15 +157,15 @@ class CspBuilder(val context: TypeEvalContext) { val originalTypeVar = instantiatedType.typeVariable return when { keepUnconstrained -> originalTypeVar - originalTypeVar.defaultType != null -> originalTypeVar.defaultType?.get() + originalTypeVar.defaultType != null -> originalTypeVar.defaultType.derefOrUnknown() originalTypeVar.bound != null -> originalTypeVar.bound - else -> null + else -> PyAnyType.unknown } } is PyTypeVarType -> { // if the solution is another PyTypeVarType, check the declared default types if (inferenceVariable.typeVariable.defaultType != null) { - return inferenceVariable.typeVariable.defaultType?.get() + return inferenceVariable.typeVariable.defaultType.derefOrUnknown() } else { return instantiatedType @@ -1435,7 +1436,7 @@ private object TypeBoundResolver { */ private fun collectLowerBounds(cp: ConstraintProblem, infVar: InferenceVariable, context: TypeEvalContext): Array { return collectBounds(cp, infVar, context) { b: TypeBound -> - (b.variance === Variance.INVARIANT) || (b.variance === Variance.CONTRAVARIANT && b.right != null) + (b.variance === Variance.INVARIANT) || (b.variance === Variance.CONTRAVARIANT && !b.right.isUnknown) } } @@ -1564,8 +1565,8 @@ private object SubtypeJudgement { /** True iff left is a subtype of right */ fun isSubtype(left: PyType?, right: PyType?, context: TypeEvalContext): Boolean { - val leftProper = if (left is PyUnconstrainedTypeVariable) left.typeVariable.defaultType?.get() else left - val rightProper = if (right is PyUnconstrainedTypeVariable) right.typeVariable.defaultType?.get() else right + val leftProper = if (left is PyUnconstrainedTypeVariable) left.typeVariable.defaultType.derefOrUnknown() else left + val rightProper = if (right is PyUnconstrainedTypeVariable) right.typeVariable.defaultType.derefOrUnknown() else right return PyTypeChecker.match(rightProper, leftProper, context) } @@ -1676,7 +1677,7 @@ private fun substituteByInferenceVars( } private fun substitutePyTypeVarTypes(original: PyType?, inferenceVars: InferenceVariablePool, context: TypeEvalContext): PyType? { - if (original == null) { + if (original.isUnknown) { return original } return PyCloningTypeVisitor.clone(original, object : PyCloningTypeVisitor(context) { @@ -1707,7 +1708,7 @@ private fun PyType?.isTopType(context: TypeEvalContext): Boolean { } private fun PyType?.isBottomType(): Boolean { - return this == null || this is PyNeverType // Any or Never + return this.isAnyOrUnknown || this is PyNeverType // Any or Never } private fun PyType?.isOptional(): Boolean { diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyUnionType.java b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyUnionType.java index fa4aca640fc8..f9afe832fc60 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyUnionType.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/types/PyUnionType.java @@ -27,6 +27,8 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import static com.jetbrains.python.psi.types.PyTypeUtilKt.isUnknown; + public class PyUnionType implements PyUnionLikeType { @@ -113,7 +115,7 @@ public class PyUnionType implements PyUnionLikeType { * @return a PyType representing the union, or null if no valid members */ public static @Nullable PyType union(@NotNull Collection<@Nullable PyType> members) { - return unionOrDefault(members, null); + return unionOrDefault(members, PyAnyType.getUnknown()); } /** @@ -155,8 +157,8 @@ public class PyUnionType implements PyUnionLikeType { * @see PyUnsafeUnionType */ public static @Nullable PyType createWeakType(@Nullable PyType type) { - if (type == null) { - return null; + if (isUnknown(type)) { + return type; } else if (type instanceof PyUnionType unionType) { if (unionType.isWeak()) { @@ -164,9 +166,9 @@ public class PyUnionType implements PyUnionLikeType { } } if (isStrictSemanticsEnabled()) { - return PyUnsafeUnionType.unsafeUnion(type, null); + return PyUnsafeUnionType.unsafeUnion(type, PyAnyType.getUnknown()); } - return union(type, null); + return union(type, PyAnyType.getUnknown()); } /** @@ -196,7 +198,7 @@ public class PyUnionType implements PyUnionLikeType { */ @Deprecated public boolean isWeak() { - return !isStrictSemanticsEnabled() && myMembers.contains(null); + return !isStrictSemanticsEnabled() && myMembers.contains(PyAnyType.getUnknown()); } /** @@ -220,7 +222,7 @@ public class PyUnionType implements PyUnionLikeType { * @return union with excluded types */ public @Nullable PyType exclude(@Nullable PyType type, @NotNull TypeEvalContext context) { - if (type == null) return excludeNull(); + if (isUnknown(type)) return excludeNull(); final List members = new ArrayList<>(); for (PyType m : getMembers()) { diff --git a/python/testSrc/com/jetbrains/python/Py3TypeTest.java b/python/testSrc/com/jetbrains/python/Py3TypeTest.java index 161a3648e8dc..831cc6d673ba 100644 --- a/python/testSrc/com/jetbrains/python/Py3TypeTest.java +++ b/python/testSrc/com/jetbrains/python/Py3TypeTest.java @@ -4618,6 +4618,21 @@ public class Py3TypeTest extends PyTestCase { """); } + @TestFor(issues = "PY-81651") + public void testEqWithNewAny() { + withNewAnyTypeEnabled(() -> { + doTest("Any", """ + from typing import Any + + class A: + def __eq__(self, other) -> Any: + return "hello :)" + + expr = A() == 1 + """); + }); + } + @TestFor(issues = "PY-84524") public void testBuiltinsCallable() { doTest("(...) -> object", """ diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index b9b6981e495a..2ad166bc4ff6 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -5,7 +5,6 @@ import com.intellij.idea.TestFor; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.util.PsiTreeUtil; @@ -6944,6 +6943,64 @@ public class PyTypingTest extends PyTestCase { }); } + public void testTypeVarDefaultAny() { + withNewAnyTypeEnabled(() -> { + doTest("Any", """ + from typing import Any + + def f[T=Any]() -> T: ... + + expr = f() + """); + }); + } + + public void testUnsolvedTypeVar() { + withNewAnyTypeEnabled(() -> { + doTest("Unknown", """ + def f[T]() -> T: ... + + expr = f() + """); + }); + } + + public void testPsiStubbedAny() { + withNewAnyTypeEnabled(() -> { + runWithAdditionalFileInLibDir("other.py", """ + from typing import Any + + x: Any + """, x -> { + myFixture.configureByText(PythonFileType.INSTANCE, """ + from other import x + + expr = x + """); + final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class); + final TypeEvalContext codeAnalysis = TypeEvalContext.codeAnalysis(expr.getProject(), expr.getContainingFile()); + assertType("Failed in code analysis context", "Any", expr, codeAnalysis); + }); + }); + } + + public void testPsiStubbedUnknown() { + withNewAnyTypeEnabled(() -> { + runWithAdditionalFileInLibDir("other.py", """ + x = asdf + """, x -> { + myFixture.configureByText(PythonFileType.INSTANCE, """ + from other import x + + expr = x + """); + final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class); + final TypeEvalContext codeAnalysis = TypeEvalContext.codeAnalysis(expr.getProject(), expr.getContainingFile()); + assertType("Failed in code analysis context", "Unknown", expr, codeAnalysis); + }); + }); + } + @TestFor(issues = "PY-84430") public void testQuotedAny() { fixme("quoted Any", AssertionError.class, "Failed in code analysis context expected:<[Any]> but was:<[Literal[0]]>", () -> @@ -7007,16 +7064,4 @@ public class PyTypingTest extends PyTestCase { final TypeEvalContext userInitiated = TypeEvalContext.userInitiated(expr.getProject(), expr.getContainingFile()).withTracing(); assertType("Failed in user initiated context", expectedType, expr, userInitiated); } - - private static void withNewAnyTypeEnabled(@NotNull Runnable test) { - var key = Registry.get("python.type.any"); - var previousValue = key.asBoolean(); - try { - key.setValue(true); - test.run(); - } - finally { - key.setValue(previousValue); - } - } } diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java index c1953ef3a7a5..b6a5f5853ccb 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java @@ -25,6 +25,7 @@ import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.impl.FilePropertyPusher; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VfsUtil; @@ -677,5 +678,17 @@ public abstract class PyTestCase extends UsefulTestCase { // the fix-me test passed -> the bug/feature was fixed! fail("Test " + comment + " was previously failing and was suppressed, but now it passes"); } + + protected static void withNewAnyTypeEnabled(@NotNull Runnable test) { + var key = Registry.get("python.type.any"); + var previousValue = key.asBoolean(); + try { + key.setValue(true); + test.run(); + } + finally { + key.setValue(previousValue); + } + } }