mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-78235 PyAnyType: add tests and refactor some more usages
GitOrigin-RevId: 9a9028d259fb33e450390653fb3984fbae43529e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
85a7b49911
commit
53e012bc7b
@@ -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 {
|
||||
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
+2
-1
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-16
@@ -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
|
||||
|
||||
+9
-6
@@ -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);
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+10
-8
@@ -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,
|
||||
|
||||
+19
-16
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+7
-9
@@ -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)
|
||||
|
||||
+9
-8
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user