PY-78235 PyAnyType: add tests and refactor some more usages

GitOrigin-RevId: 9a9028d259fb33e450390653fb3984fbae43529e
This commit is contained in:
Morgan Bartholomew
2026-03-22 07:40:27 +00:00
committed by intellij-monorepo-bot
parent 85a7b49911
commit 53e012bc7b
23 changed files with 214 additions and 120 deletions
@@ -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 <tt>intellij.python.psi</tt> 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<T> {
}
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 {
@@ -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 {
@@ -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()
}
}
}
@@ -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<Context?>() {
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<Context?>() {
) {
typeHint = typeHint.qualifier!!
}
val type = Ref.deref<PyType?>(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<Context?>() {
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<Context?>() {
}
}
val annotatedType: Ref<PyType?>? = getTypeFromTypeHint<PyTargetExpression?>(referenceTarget, context)
val annotatedType = getTypeFromTypeHint(referenceTarget, context)
if (annotatedType != null) {
return annotatedType
}
@@ -991,8 +992,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext<Context?>() {
.collect(PyTypeUtil.toUnionFromRef())
}
private fun <T>
getTypeFromTypeHint(element: T, context: Context): Ref<PyType?>? where T : PyAnnotationOwner?, T : PyTypeCommentOwner? {
private fun <T> getTypeFromTypeHint(element: T, context: Context): Ref<PyType?>? 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<Context?>() {
private fun evaluateSuperClassesAsTypeHints(pyClass: PyClass, context: TypeEvalContext): MutableList<PyClassType> {
val results: MutableList<PyClassType> = ArrayList()
for (superClassExpression in getSuperClassExpressions(pyClass)) {
val type = Ref.deref<PyType?>(getType(superClassExpression, context))
val type = getType(superClassExpression, context).derefOrUnknown()
if (type is PyClassType) {
results.add(type)
}
@@ -1394,7 +1394,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext<Context?>() {
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<Context?>() {
private fun getTypeEngineType(typeHint: PyExpression, context: Context): Ref<PyType?>? {
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<Context?>() {
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<Context?>() {
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<Context?>() {
) {
return Ref(type.map { (it as PyClassType).toClass() })
}
return Ref()
return Ref(PyAnyType.unknown)
}
private fun getAnyType(element: PsiElement, context: Context): Ref<PyType?>? {
@@ -1695,7 +1695,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext<Context?>() {
return Ref(PyUnionType.union(typeRef.get(), getInstance(element).noneType))
}
}
return Ref()
return Ref(PyAnyType.unknown)
}
}
return null
@@ -2121,7 +2121,7 @@ class PyTypingTypeProvider : PyTypeProviderWithCustomContext<Context?>() {
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<Context?>() {
.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<Context?>() {
defaultType = if (defaultExprWithoutParens != null)
getTypePreventingRecursion(defaultExprWithoutParens, context)
else
Ref()
Ref(PyAnyType.unknown)
}
val declarationElement = element as? PyQualifiedNameOwner
@@ -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
@@ -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<PyCallableType>.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<PyType?> {
@@ -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<PyClassStub> 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);
}
@@ -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<PyFunctionStub> implements PyFunction {
@@ -221,7 +223,7 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> 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<PyType, PyType> yieldSendTypePair = getYieldExpressionType(context);
@@ -307,14 +309,14 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> 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);
@@ -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);
@@ -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<PyNamedParameterStub
}
}
}
return null;
return PyAnyType.getUnknown();
}
@Override
@@ -56,6 +56,7 @@ import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
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.PyCallableType;
import com.jetbrains.python.psi.types.PyClassLikeType;
import com.jetbrains.python.psi.types.PyClassType;
@@ -86,6 +87,7 @@ import java.util.function.Predicate;
import static com.jetbrains.python.psi.impl.PyCallExpressionHelper.getCalleeType;
import static com.jetbrains.python.psi.types.PyNoneTypeKt.isNoneType;
import static com.jetbrains.python.psi.types.PyTypeUtilKt.isUnknown;
/**
* Implements reference expression PSI.
@@ -232,7 +234,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
final boolean qualified = isQualified();
final PyType providedType = getTypeFromProviders(context);
if (providedType != null) {
if (!isUnknown(providedType)) {
return providedType;
}
@@ -246,9 +248,9 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
// null means no result; Ref(null) here can mean that a variable is annotated
// like `var: Any` earlier, so we know not to use __getattr__ later
final Ref<PyType> 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<PyType> 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<PyType> 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<PyType> getReferenceTypeFromProviders(@NotNull PsiElement target,
@@ -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<PyTargetExpressionStub> implements PyTargetExpression {
private volatile @Nullable QualifiedName myQualifiedName;
@@ -147,14 +150,14 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
if (PyNames.ALL.equals(getName())) {
// no type for __all__, to avoid unresolved reference errors for expressions where a qualifier is a name
// imported via __all__
return null;
return PyAnyType.getUnknown();
}
final Ref<PyType> 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<PyTargetExpression
return PyUnionType.union(types);
}
type = getTypeFromComment(this);
if (type != null) {
if (!isUnknown(type)) {
return type;
}
final PsiElement parent = PsiTreeUtil.skipParentsOfType(this, PyParenthesizedExpression.class);
@@ -203,7 +206,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
}
return context.getType(assignedValue);
}
return null;
return PyAnyType.getUnknown();
}
if (parent instanceof PyTupleExpression || parent instanceof PyListLiteralExpression) {
PsiElement nextParent =
@@ -261,7 +264,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
if (excType != null) {
return excType;
}
return null;
return PyAnyType.getUnknown();
}
private @Nullable PyType getTargetTypeFromIterableUnpacking(@NotNull PySequenceExpression topmostContainingTupleOrList,
@@ -297,7 +300,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
}
}
}
return null;
return PyAnyType.getUnknown();
}
@Override
@@ -342,7 +345,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
// Guess the return type of __enter__
return PyUnionType.createWeakType(withType);
}
return null;
return PyAnyType.getUnknown();
}
public @Nullable PyType getTypeFromDocString() {
@@ -367,7 +370,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
if (typeName != null) {
return PyTypeParser.getTypeByName(this, typeName);
}
return null;
return PyAnyType.getUnknown();
}
public static @Nullable PyType getTypeFromComment(PyTargetExpressionImpl targetExpression) {
@@ -382,7 +385,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
return PyTypeParser.getTypeByName(targetExpression, typeName);
}
}
return null;
return PyAnyType.getUnknown();
}
private @Nullable PyType getTypeFromIteration(@NotNull TypeEvalContext context) {
@@ -421,7 +424,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
return type;
}
}
return null;
return PyAnyType.getUnknown();
}
private static @Nullable PyType getIterationType(@Nullable PyType iterableType,
@@ -434,7 +437,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
if (type instanceof PyCollectionType collectionType) {
return collectionType.getIteratedItemType();
}
return null;
return PyAnyType.getUnknown();
}
// TODO migrate this to matching against typing.Iterable protocol with PyTypeUtil.convertToType
@@ -447,7 +450,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
return ((PyUnionType)iterableType).map(member -> 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<PyTargetExpression
}
}
}
return null;
return PyAnyType.getUnknown();
}
private static @Nullable PyType getIteratedItemType(@Nullable PyType type,
@@ -490,7 +493,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
if (type instanceof PyCollectionType) {
return ((PyCollectionType)type).getIteratedItemType();
}
return null;
return PyAnyType.getUnknown();
}
private static @Nullable Ref<PyType> getNextMethodCallType(@Nullable PyType type,
@@ -572,7 +575,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
private @Nullable PyType getTypeFromExcept() {
PyExceptPart exceptPart = PsiTreeUtil.getParentOfType(this, PyExceptPart.class);
if (exceptPart == null || exceptPart.getTarget() != this) {
return null;
return PyAnyType.getUnknown();
}
final PyExpression exceptClass = exceptPart.getExceptClass();
if (exceptClass instanceof PyReferenceExpression) {
@@ -581,7 +584,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
return new PyClassTypeImpl((PyClass)element, false);
}
}
return null;
return PyAnyType.getUnknown();
}
@Override
@@ -8,6 +8,7 @@ import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyYieldExpression;
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.NotNull;
@@ -72,6 +73,6 @@ public class PyYieldExpressionImpl extends PyElementImpl implements PyYieldExpre
}
return PyBuiltinCache.getInstance(this).getNoneType();
}
return null;
return PyAnyType.getUnknown();
}
}
@@ -202,6 +202,7 @@ object PyExpectedTypeJudgement {
?: fromAssignment(expr, ctx)
?: fromYield(expr, ctx)
?: fromReturn(expr, ctx)
?: PyAnyType.unknown
}
private fun fromArgument(callArgument: PyExpression, ctx: TypeEvalContext): PyType? {
@@ -96,7 +96,7 @@ class PyLiteralType private constructor(cls: PyClass, val expression: PyExpressi
private class TypePromoter(private val context: TypeEvalContext, private val inferLiteralTypes: Boolean) {
fun promoteToType(expectedType: PyType?, expression: PyExpression): PyType? {
val value = PyUtil.peelArgument(expression) ?: return null
val value = PyUtil.peelArgument(expression) ?: return PyAnyType.unknown
return when (value) {
is PyDictLiteralExpression -> {
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)
}
@@ -60,7 +60,7 @@ public final class PySyntheticCallHelper {
@NotNull List<PyType> argumentTypes,
@NotNull TypeEvalContext context) {
List<PyFunction> 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))
@@ -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)
@@ -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)
@@ -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<PyType?> {
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 {
@@ -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<PyType> members = new ArrayList<>();
for (PyType m : getMembers()) {
@@ -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", """
@@ -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);
}
}
}
@@ -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);
}
}
}