mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-76886, PY-76860 Make PySelfType extend PyClassType
to be able to access its scope class through getPyClass() and infer it
for `self` parameters, not having to replace it everywhere with
PyClassType.
This is similar to how other types with a "backing" PyClass are implemented, e.g.
PyTypedDictType delegates to PyClass(dict), PyNarrowedType delegates to PyClass(bool),
and so on.
It allowed removing special-casing for PySelfType and in many places treat
it identically to its scope class (that was previously inferred for `self`),
without converting back and forth between the two.
The only exceptions are places where we actually need to access the scope
class for error messages and code generation, e.g. in
PyUnresolvedReferencesInspection and its quick fixes.
Namely, extractScopeClassTypeIfNeeded and mutable `matchingScope`
were removed from PySelfType.
PyTypeChecker.match was simplified to consider PySelfType a subtype
of its scope PyClassType, itself compatible only with another PySelfType
(if a Self type is expected, only another Self type can be passed).
Since PySelf type now propagates through type inference freely,
not replaced with PyClassType when unnecessary, there is no need
to track where it's used. In particular, situations like
the following are properly handled because calling a method
returning Self on `self` still returns Self, but calling it
on an instance of the scope class returns PyClass for this class.
```
class C:
def copy(self):
return self
def m(self):
x: Self = self.copy() # ok
x: Self = C().copy() # expected Self@C, actual C
```
Few places in the type checker unification logic were updated to
take into account that the type of `self` for generic classes is
now just PySelfType, not PyCollectionType with unsubstituted type
arguments.
Among other benefits, this change allowed removing special-casing
for `object.__class__`. Now its type is just `type[Self]`
according to the type hints in Typeshed.
One unexpected place that required an update was resolveMember
and getVariants for PyCallableType.
Because the type `self` parameter is now PySelfType, which
implements PyTypeParameter, types of all unbound methods are now considered
generic types and hence go through the substitution process,
that replaces PyFunctionType with PyCallableTypeImpl (to be revised separately).
Surprisingly, PyCallableType didn't provide any of the attributes
of `types.FunctionType` or `types.UnboundMethodType` as PyFunctionType did.
Now the two callable types are in sync.
GitOrigin-RevId: eddb56f2fe1f2694554943fc64df288da6d84670
This commit is contained in:
committed by
intellij-monorepo-bot
parent
442fab7b6c
commit
a6ba6900ff
+1
-27
@@ -127,10 +127,6 @@ public class PyTypeCheckerInspection extends PyInspection {
|
||||
}
|
||||
|
||||
PyType actual = returnExpr != null ? tryPromotingType(returnExpr, expected) : PyBuiltinCache.getInstance(node).getNoneType();
|
||||
if (returnExpr != null) {
|
||||
expected = trySetSelfMatchingScope(expected, returnExpr, myTypeEvalContext);
|
||||
}
|
||||
|
||||
if (!PyTypeChecker.match(expected, actual, myTypeEvalContext)) {
|
||||
final String expectedName = PythonDocumentationProvider.getVerboseTypeName(expected, myTypeEvalContext);
|
||||
final String actualName = PythonDocumentationProvider.getTypeName(actual, myTypeEvalContext);
|
||||
@@ -280,9 +276,6 @@ public class PyTypeCheckerInspection extends PyInspection {
|
||||
}
|
||||
|
||||
final PyType actual = tryPromotingType(value, expected);
|
||||
|
||||
expected = trySetSelfMatchingScope(expected, value, myTypeEvalContext);
|
||||
|
||||
if (!PyTypeChecker.match(expected, actual, myTypeEvalContext)) {
|
||||
String expectedName = PythonDocumentationProvider.getVerboseTypeName(expected, myTypeEvalContext);
|
||||
String actualName = PythonDocumentationProvider.getTypeName(actual, myTypeEvalContext);
|
||||
@@ -356,25 +349,6 @@ public class PyTypeCheckerInspection extends PyInspection {
|
||||
return myTypeEvalContext.getType(value);
|
||||
}
|
||||
|
||||
private @Nullable PyType trySetSelfMatchingScope(@Nullable PyType type, @NotNull PyExpression anchor, @NotNull TypeEvalContext context) {
|
||||
if (type != null) {
|
||||
ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(anchor);
|
||||
if (scopeOwner instanceof PyFunction function) {
|
||||
scopeOwner = function.getContainingClass();
|
||||
}
|
||||
if (scopeOwner instanceof PyClass pyClass) {
|
||||
return PyCloningTypeVisitor.clone(type, new PyCloningTypeVisitor(context) {
|
||||
@Override
|
||||
public PyType visitPySelfType(@NotNull PySelfType selfType) {
|
||||
selfType.setMatchingScope(pyClass);
|
||||
return super.visitPySelfType(selfType);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyFunction(@NotNull PyFunction node) {
|
||||
final PyAnnotation annotation = node.getAnnotation();
|
||||
@@ -505,7 +479,7 @@ public class PyTypeCheckerInspection extends PyInspection {
|
||||
for (Map.Entry<PyExpression, PyCallableParameter> entry : regularMappedParameters.entrySet()) {
|
||||
final PyExpression argument = entry.getKey();
|
||||
final PyCallableParameter parameter = entry.getValue();
|
||||
final PyType expected = trySetSelfMatchingScope(parameter.getArgumentType(myTypeEvalContext), argument, myTypeEvalContext);
|
||||
final PyType expected = parameter.getArgumentType(myTypeEvalContext);
|
||||
final PyType promotedToLiteral = PyLiteralType.Companion.promoteToLiteral(argument, expected, myTypeEvalContext, substitutions);
|
||||
final var actual = promotedToLiteral != null ? promotedToLiteral : myTypeEvalContext.getType(argument);
|
||||
|
||||
|
||||
+2
-1
@@ -209,7 +209,8 @@ class PyTypeHintsInspection : PyInspection() {
|
||||
}
|
||||
|
||||
if (node.referencedName == PyNames.CANONICAL_SELF) {
|
||||
val typeName = myTypeEvalContext.getType(node)?.name
|
||||
val refType = myTypeEvalContext.getType(node)
|
||||
val typeName = (if (refType is PySelfType) refType.scopeClassType else refType)?.name
|
||||
if (typeName != null && typeName != PyNames.CANONICAL_SELF) {
|
||||
registerProblem(node, PyPsiBundle.message("INSP.type.hints.invalid.type.self"), ProblemHighlightType.GENERIC_ERROR, null,
|
||||
ReplaceWithTypeNameQuickFix(typeName))
|
||||
|
||||
+10
-5
@@ -79,8 +79,8 @@ public abstract class PyUnresolvedReferencesVisitor extends PyInspectionVisitor
|
||||
final PyExpression qualifier = node.getQualifier();
|
||||
final String attrName = node.getReferencedName();
|
||||
if (qualifier != null && attrName != null) {
|
||||
final PyType type = myTypeEvalContext.getType(qualifier);
|
||||
if (PySelfType.extractScopeClassTypeIfNeeded(type) instanceof PyClassType classType &&
|
||||
final PyType type = replaceSelfWithItsScopeClass(myTypeEvalContext.getType(qualifier));
|
||||
if (type instanceof PyClassType classType &&
|
||||
!classType.isAttributeWritable(attrName, myTypeEvalContext)) {
|
||||
final ASTNode nameNode = node.getNameElement();
|
||||
final PsiElement e = nameNode != null ? nameNode.getPsi() : node;
|
||||
@@ -89,6 +89,10 @@ public abstract class PyUnresolvedReferencesVisitor extends PyInspectionVisitor
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable PyType replaceSelfWithItsScopeClass(@Nullable PyType type) {
|
||||
return type instanceof PySelfType selfType ? selfType.getScopeClassType() : type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyElement(final @NotNull PyElement node) {
|
||||
super.visitPyElement(node);
|
||||
@@ -286,7 +290,7 @@ public abstract class PyUnresolvedReferencesVisitor extends PyInspectionVisitor
|
||||
}
|
||||
final PyExpression qualifier = getReferenceQualifier(reference);
|
||||
if (qualifier != null) {
|
||||
final PyType type = myTypeEvalContext.getType(qualifier);
|
||||
PyType type = replaceSelfWithItsScopeClass(myTypeEvalContext.getType(qualifier));
|
||||
if (type != null) {
|
||||
if (ignoreUnresolvedMemberForType(type, reference, refName) || isDeclaredInSlots(type, refName)) {
|
||||
return;
|
||||
@@ -306,6 +310,7 @@ public abstract class PyUnresolvedReferencesVisitor extends PyInspectionVisitor
|
||||
((PyOperatorReference)reference).getReadableOperatorName());
|
||||
}
|
||||
else {
|
||||
// TODO use proper type rendering here
|
||||
description = PyPsiBundle.message("INSP.unresolved.refs.unresolved.attribute.for.class", refText, type.getName());
|
||||
}
|
||||
}
|
||||
@@ -792,8 +797,8 @@ public abstract class PyUnresolvedReferencesVisitor extends PyInspectionVisitor
|
||||
@NotNull String refText) {
|
||||
List<LocalQuickFix> result = new ArrayList<>();
|
||||
PsiElement element = reference.getElement();
|
||||
if (type instanceof PyClassTypeImpl) {
|
||||
PyClass cls = ((PyClassType)type).getPyClass();
|
||||
if (type instanceof PyClassType pyClassType) {
|
||||
PyClass cls = pyClassType.getPyClass();
|
||||
if (!PyBuiltinCache.getInstance(element).isBuiltin(cls)) {
|
||||
if (element.getParent() instanceof PyCallExpression) {
|
||||
result.add(new AddMethodQuickFix(refText, cls.getName(), true));
|
||||
|
||||
@@ -182,37 +182,6 @@ public final class PyUtil {
|
||||
|
||||
// TODO: move to a more proper place?
|
||||
|
||||
/**
|
||||
* Determine the type of a special attribute. Currently supported: {@code __class__} and {@code __dict__}.
|
||||
*
|
||||
* @param ref reference to a possible attribute; only qualified references make sense.
|
||||
* @return type, or null (if type cannot be determined, reference is not to a known attribute, etc.)
|
||||
*/
|
||||
public static @Nullable PyType getSpecialAttributeType(@Nullable PyReferenceExpression ref, TypeEvalContext context) {
|
||||
if (ref != null) {
|
||||
PyExpression qualifier = ref.getQualifier();
|
||||
if (qualifier != null) {
|
||||
String attr_name = ref.getReferencedName();
|
||||
if (PyNames.__CLASS__.equals(attr_name)) {
|
||||
PyType qualifierType = context.getType(qualifier);
|
||||
if (qualifierType instanceof PyClassType qClassType) {
|
||||
return new PyClassTypeImpl(qClassType.getPyClass(), true); // always as class, never instance
|
||||
}
|
||||
if (qualifierType instanceof PySelfType selfType) {
|
||||
return selfType.toClass();
|
||||
}
|
||||
}
|
||||
else if (PyNames.DUNDER_DICT.equals(attr_name)) {
|
||||
PyType qualifierType = context.getType(qualifier);
|
||||
if (qualifierType instanceof PyClassType qClassType && qClassType.isDefinition()) {
|
||||
return PyBuiltinCache.getInstance(ref).getDictType();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that 'thing' is not null; else throws an {@link IncorrectOperationException}.
|
||||
*
|
||||
@@ -1189,6 +1158,50 @@ public final class PyUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static @Nullable PyClassType selectCallableTypeRuntimeClass(@NotNull PyCallableType callableType,
|
||||
@Nullable PyExpression location,
|
||||
@NotNull TypeEvalContext context) {
|
||||
String className;
|
||||
if (location instanceof PyReferenceExpression re && isBoundMethodReference(callableType, re, context)) {
|
||||
className = PyNames.TYPES_METHOD_TYPE;
|
||||
}
|
||||
else {
|
||||
className = PyNames.TYPES_FUNCTION_TYPE;
|
||||
}
|
||||
PyCallable callable = callableType.getCallable();
|
||||
PyClass cls = callable != null ? PyPsiFacade.getInstance(callable.getProject()).createClassByQName(className, callable) : null;
|
||||
return cls != null ? new PyClassTypeImpl(cls, false) : null;
|
||||
}
|
||||
|
||||
private static boolean isBoundMethodReference(@NotNull PyCallableType callableType,
|
||||
@NotNull PyReferenceExpression location,
|
||||
@NotNull TypeEvalContext context) {
|
||||
final PyFunction function = as(callableType.getCallable(), PyFunction.class);
|
||||
final boolean isNonStaticMethod = function != null && function.getContainingClass() != null && function.getModifier() != STATICMETHOD;
|
||||
if (isNonStaticMethod) {
|
||||
// In Python 2 unbound methods have __method fake type
|
||||
if (LanguageLevel.forElement(location).isPython2()) {
|
||||
return true;
|
||||
}
|
||||
final PyExpression qualifier;
|
||||
if (location.isQualified()) {
|
||||
qualifier = location.getQualifier();
|
||||
}
|
||||
else {
|
||||
final PyResolveContext resolveContext = PyResolveContext.defaultContext(context);
|
||||
qualifier = ContainerUtil.getLastItem(location.followAssignmentsChain(resolveContext).getQualifiers());
|
||||
}
|
||||
if (qualifier != null) {
|
||||
final PyType qualifierType = context.getType(qualifier);
|
||||
if (PyTypeUtil.toStream(qualifierType).select(PyClassType.class).anyMatch(it -> !it.isDefinition())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final class MethodFlags {
|
||||
|
||||
private final boolean myIsStaticMethod;
|
||||
|
||||
@@ -162,7 +162,14 @@ private fun PyCallExpression.getExplicitResolveResults(resolveContext: PyResolve
|
||||
val result = mutableListOf<PyCallableType>()
|
||||
|
||||
for (type in PyTypeUtil.toStream(calleeType)) {
|
||||
if (type is PyClassType) {
|
||||
// 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.
|
||||
// See e.g. Py3TypeCheckerInspectionTest.testSelfInClassMethods
|
||||
if (type is PySelfType) {
|
||||
result.add(type)
|
||||
}
|
||||
else if (type is PyClassType) {
|
||||
val implicitlyInvokedMethods = type
|
||||
.resolveImplicitlyInvokedMethods(this, resolveContext)
|
||||
.forEveryScopeTakeOverloadsOtherwiseImplementations(context)
|
||||
@@ -194,7 +201,7 @@ private fun PyCallExpression.getImplicitResolveResults(resolveContext: PyResolve
|
||||
val qualifier = callee.qualifier
|
||||
if (qualifier == null || !qualifier.canQualifyAnImplicitName()) return listOf()
|
||||
|
||||
val qualifierType = PySelfType.extractScopeClassTypeIfNeeded(context.getType(qualifier))
|
||||
val qualifierType = context.getType(qualifier)
|
||||
if (PyTypeChecker.isUnknown(qualifierType, context) ||
|
||||
qualifierType is PyStructuralType && qualifierType.isInferredFromUsages
|
||||
) {
|
||||
@@ -407,7 +414,7 @@ private fun PyCallable?.isQualifiedByInstance(qualifier: PyExpression, context:
|
||||
}
|
||||
|
||||
private fun PyCallable?.isQualifiedByClass(qualifier: PyExpression, context: TypeEvalContext): Boolean {
|
||||
val qualifierType = PySelfType.extractScopeClassTypeIfNeeded(context.getType(qualifier))
|
||||
val qualifierType = context.getType(qualifier)
|
||||
|
||||
if (qualifierType is PyClassType) {
|
||||
return qualifierType.isDefinition() && belongsToSpecifiedClassHierarchy(qualifierType.pyClass, context)
|
||||
@@ -594,7 +601,7 @@ private fun PyCallExpression.getSuperCallType(context: TypeEvalContext): Maybe<P
|
||||
return Maybe(getSuperCallTypeForArguments(context, possible_class, args[1]))
|
||||
}
|
||||
if (possible_class is PyNamedParameter) {
|
||||
val paramType = PySelfType.extractScopeClassTypeIfNeeded(context.getType(possible_class))
|
||||
val paramType = context.getType(possible_class)
|
||||
if (paramType is PyClassType) {
|
||||
return Maybe(getSuperCallTypeForArguments(context, paramType.pyClass, args[1]))
|
||||
}
|
||||
@@ -609,7 +616,7 @@ private fun PyCallExpression.getSuperCallType(context: TypeEvalContext): Maybe<P
|
||||
private fun getSuperCallTypeForArguments(context: TypeEvalContext, firstClass: PyClass, second_arg: PyExpression?): PyType? {
|
||||
// check 2nd argument, too; it should be an instance
|
||||
if (second_arg != null) {
|
||||
val second_type = PySelfType.extractScopeClassTypeIfNeeded(context.getType(second_arg));
|
||||
val second_type = context.getType(second_arg);
|
||||
if (second_type is PyClassType) {
|
||||
// imitate isinstance(second_arg, possible_class)
|
||||
val secondClass = second_type.pyClass
|
||||
|
||||
+4
-17
@@ -234,9 +234,6 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
|
||||
final PyType qualifierType = context.getType(qualifier);
|
||||
|
||||
final PyType dunderClassType = getDunderClassType(qualifierType, attrName);
|
||||
if (dunderClassType != null) return Ref.create(dunderClassType);
|
||||
|
||||
final Ref<PyType> typeOfProperty = getTypeOfProperty(qualifierType, attrName, context);
|
||||
if (typeOfProperty != null) {
|
||||
return typeOfProperty;
|
||||
@@ -272,10 +269,9 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
final PyExpression qualifier = getQualifier();
|
||||
assert qualifier != null;
|
||||
|
||||
final PyType qType = PySelfType.extractScopeClassTypeIfNeeded(context.getType(qualifier));
|
||||
if (qType instanceof PyClassType classType) {
|
||||
if (context.getType(qualifier) instanceof PyClassLikeType classLikeType) {
|
||||
final ResolveResult getattr = ContainerUtil.getFirstItem(
|
||||
classType.resolveMember(PyNames.GETATTR, qualifier, AccessDirection.READ, PyResolveContext.defaultContext(context)));
|
||||
classLikeType.resolveMember(PyNames.GETATTR, qualifier, AccessDirection.READ, PyResolveContext.defaultContext(context)));
|
||||
if (getattr != null && getattr.getElement() instanceof PyCallable method) {
|
||||
return context.getReturnType(method);
|
||||
}
|
||||
@@ -323,7 +319,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
}
|
||||
|
||||
private @Nullable Ref<PyType> getTypeOfProperty(@Nullable PyType qualifierType, @NotNull String name, @NotNull TypeEvalContext context) {
|
||||
if (PySelfType.extractScopeClassTypeIfNeeded(qualifierType) instanceof PyClassType classType) {
|
||||
if (qualifierType instanceof PyClassType classType) {
|
||||
final PyClass pyClass = classType.getPyClass();
|
||||
final Property property = pyClass.findProperty(name, true, context);
|
||||
|
||||
@@ -360,14 +356,6 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @Nullable PyType getDunderClassType(@Nullable PyType qualifierType, @NotNull String attrName) {
|
||||
if (qualifierType instanceof PyClassType classType && PyNames.__CLASS__.equals(attrName)) {
|
||||
// PyInstantiableType#toClass() does not work here, as we also need to remove generic parameters
|
||||
return new PyClassTypeImpl(classType.getPyClass(), true);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private @Nullable PyType getTypeFromProviders(@NotNull TypeEvalContext context) {
|
||||
for (PyTypeProvider provider : PyTypeProvider.EP_NAME.getExtensionList()) {
|
||||
try {
|
||||
@@ -489,8 +477,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
@NotNull TypeEvalContext context,
|
||||
@NotNull PyReferenceExpression anchor) {
|
||||
if (type instanceof PyFunctionType functionType && context.maySwitchToAST(anchor) && anchor.getQualifier() != null) {
|
||||
PyType qualifierType = PySelfType.extractScopeClassTypeIfNeeded(context.getType(anchor.getQualifier()));
|
||||
if (qualifierType instanceof PyClassLikeType classLikeType && classLikeType.isDefinition() &&
|
||||
if (context.getType(anchor.getQualifier()) instanceof PyClassLikeType classLikeType && classLikeType.isDefinition() &&
|
||||
functionType.getModifier() != PyAstFunction.Modifier.CLASSMETHOD) {
|
||||
return type;
|
||||
}
|
||||
|
||||
+8
-1
@@ -348,6 +348,7 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO migrate this to matching against typing.Iterable protocol with PyTypeUtil.convertToType
|
||||
public static @Nullable PyType getIterationType(@Nullable PyType iterableType, @Nullable PyExpression source, @NotNull PsiElement anchor,
|
||||
@NotNull TypeEvalContext context) {
|
||||
if (iterableType instanceof PyTupleType tupleType) {
|
||||
@@ -450,7 +451,13 @@ public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpression
|
||||
PyCallableParameter firstParameter = parameters.get(0);
|
||||
if (!firstParameter.isSelf()) return false;
|
||||
PyType selfParameterType = firstParameter.getType(context);
|
||||
return PyTypeChecker.match(selfParameterType, receiverType, context);
|
||||
// See
|
||||
// - PyTypingTest.testForLoopTargetTypeComesFromCorrectDunderIterOverload
|
||||
// - PyTypingTest.testIterationOverRegularStrEmitsStrNotLiteralString
|
||||
// If `self` in an overload was annotated with an explicit type hint (e.g. a subclass), leave it as-is,
|
||||
// otherwise replace Self type with an actual instance type.
|
||||
PyTypeChecker.GenericSubstitutions substitutions = PyTypeChecker.unifyReceiver(receiverType, context);
|
||||
return PyTypeChecker.match(PyTypeChecker.substitute(selfParameterType, substitutions, context), receiverType, context);
|
||||
}
|
||||
|
||||
public static @Nullable PyType getContextSensitiveType(@NotNull PyFunction function, @NotNull TypeEvalContext context,
|
||||
|
||||
+1
-2
@@ -151,7 +151,7 @@ public class PyQualifiedReference extends PyReferenceImpl {
|
||||
|
||||
private void addDocReference(ResolveResultList ret, PyExpression qualifier, PyType qualifierType) {
|
||||
PsiElement docstring = null;
|
||||
if (PySelfType.extractScopeClassTypeIfNeeded(qualifierType) instanceof PyClassType classType) {
|
||||
if (qualifierType instanceof PyClassType classType) {
|
||||
PyClass qualClass = classType.getPyClass();
|
||||
docstring = qualClass.getDocStringExpression();
|
||||
}
|
||||
@@ -180,7 +180,6 @@ public class PyQualifiedReference extends PyReferenceImpl {
|
||||
final PyQualifiedExpression element = CompletionUtilCoreImpl.getOriginalOrSelf(myElement);
|
||||
|
||||
PyType qualifierType = TypeEvalContext.codeCompletion(element.getProject(), element.getContainingFile()).getType(qualifier);
|
||||
qualifierType = PySelfType.extractScopeClassTypeIfNeeded(qualifierType);
|
||||
ProcessingContext ctx = new ProcessingContext();
|
||||
final Set<String> namesAlready = new HashSet<>();
|
||||
ctx.put(PyType.CTX_NAMES, namesAlready);
|
||||
|
||||
@@ -14,10 +14,7 @@ import com.jetbrains.python.psi.resolve.RatedResolveResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
|
||||
public class PyCallableTypeImpl implements PyCallableType {
|
||||
private final @Nullable List<PyCallableParameter> myParameters;
|
||||
@@ -83,12 +80,17 @@ public class PyCallableTypeImpl implements PyCallableType {
|
||||
@Nullable PyExpression location,
|
||||
@NotNull AccessDirection direction,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
return null;
|
||||
PyClassType delegate = PyUtil.selectCallableTypeRuntimeClass(this, location, resolveContext.getTypeEvalContext());
|
||||
return delegate != null ? delegate.resolveMember(name, location, direction, resolveContext) : Collections.emptyList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
@Override
|
||||
public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) {
|
||||
return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
|
||||
TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(location.getProject(), location.getContainingFile());
|
||||
PyExpression callee = location instanceof PyReferenceExpression re ? re.getQualifier() : null;
|
||||
PyClassType delegate = PyUtil.selectCallableTypeRuntimeClass(this, callee, typeEvalContext);
|
||||
return delegate != null ? delegate.getCompletionVariants(completionPrefix, location, context) : ArrayUtilRt.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,7 +24,7 @@ public final class PyDescriptorTypeUtil {
|
||||
@Nullable PyType attributeType,
|
||||
@NotNull TypeEvalContext context) {
|
||||
if (!expression.isQualified()) return null;
|
||||
final PyClassLikeType targetType = as(PySelfType.extractScopeClassTypeIfNeeded(attributeType), PyClassLikeType.class);
|
||||
final PyClassLikeType targetType = as(attributeType, PyClassLikeType.class);
|
||||
if (targetType == null || targetType.isDefinition()) return null;
|
||||
|
||||
final PyResolveContext resolveContext = PyResolveContext.noProperties(context);
|
||||
@@ -38,7 +38,7 @@ public final class PyDescriptorTypeUtil {
|
||||
public static @Nullable Ref<PyType> getExpectedValueTypeForDunderSet(@NotNull PyTargetExpression targetExpression,
|
||||
@Nullable PyType attributeType,
|
||||
@NotNull TypeEvalContext context) {
|
||||
final PyClassLikeType targetType = as(PySelfType.extractScopeClassTypeIfNeeded(attributeType), PyClassLikeType.class);
|
||||
final PyClassLikeType targetType = as(attributeType, PyClassLikeType.class);
|
||||
if (targetType == null || targetType.isDefinition()) return null;
|
||||
|
||||
final PyResolveContext resolveContext = PyResolveContext.noProperties(context);
|
||||
@@ -54,8 +54,8 @@ public final class PyDescriptorTypeUtil {
|
||||
@NotNull TypeEvalContext context) {
|
||||
PyExpression qualifier = expression.getQualifier();
|
||||
if (qualifier != null && attributeType instanceof PyCallableType receiverType) {
|
||||
PyType qualifierType = PySelfType.extractScopeClassTypeIfNeeded(context.getType(qualifier));
|
||||
if (qualifierType instanceof PyClassType classType) {
|
||||
PyType qualifierType = context.getType(qualifier);
|
||||
if (qualifierType instanceof PyClassLikeType classType) {
|
||||
PyType instanceArgumentType;
|
||||
PyType instanceTypeArgument;
|
||||
final var noneType = PyBuiltinCache.getInstance(expression).getNoneType();
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.RatedResolveResult;
|
||||
@@ -16,9 +15,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.jetbrains.python.ast.PyAstFunction.Modifier.STATICMETHOD;
|
||||
import static com.jetbrains.python.psi.PyUtil.as;
|
||||
|
||||
/**
|
||||
* Type of a particular function that is represented as a {@link PyCallable} in the PSI tree.
|
||||
*/
|
||||
@@ -71,70 +67,21 @@ public class PyFunctionTypeImpl implements PyFunctionType {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends RatedResolveResult> resolveMember(@NotNull String name,
|
||||
@Nullable PyExpression location,
|
||||
@NotNull AccessDirection direction,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
final PyClassType delegate = selectCallableType(location, resolveContext.getTypeEvalContext());
|
||||
if (delegate == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return delegate.resolveMember(name, location, direction, resolveContext);
|
||||
public @Nullable List<? extends RatedResolveResult> resolveMember(@NotNull String name,
|
||||
@Nullable PyExpression location,
|
||||
@NotNull AccessDirection direction,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
PyClassType delegate = PyUtil.selectCallableTypeRuntimeClass(this, location, resolveContext.getTypeEvalContext());
|
||||
return delegate != null ? delegate.resolveMember(name, location, direction, resolveContext) : Collections.emptyList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
@Override
|
||||
public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) {
|
||||
final TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(location.getProject(), location.getContainingFile());
|
||||
final PyClassType delegate;
|
||||
if (location instanceof PyReferenceExpression) {
|
||||
delegate = selectCallableType(((PyReferenceExpression)location).getQualifier(), typeEvalContext);
|
||||
}
|
||||
else {
|
||||
final PyClass cls = PyPsiFacade.getInstance(myCallable.getProject()).createClassByQName(PyNames.TYPES_FUNCTION_TYPE, myCallable);
|
||||
delegate = cls != null ? new PyClassTypeImpl(cls, false) : null;
|
||||
}
|
||||
if (delegate == null) {
|
||||
return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
return delegate.getCompletionVariants(completionPrefix, location, context);
|
||||
}
|
||||
|
||||
private @Nullable PyClassType selectCallableType(@Nullable PyExpression location, @NotNull TypeEvalContext context) {
|
||||
final String className;
|
||||
if (location instanceof PyReferenceExpression && isBoundMethodReference((PyReferenceExpression)location, context)) {
|
||||
className = PyNames.TYPES_METHOD_TYPE;
|
||||
}
|
||||
else {
|
||||
className = PyNames.TYPES_FUNCTION_TYPE;
|
||||
}
|
||||
final PyClass cls = PyPsiFacade.getInstance(myCallable.getProject()).createClassByQName(className, myCallable);
|
||||
return cls != null ? new PyClassTypeImpl(cls, false) : null;
|
||||
}
|
||||
|
||||
private boolean isBoundMethodReference(@NotNull PyReferenceExpression location, @NotNull TypeEvalContext context) {
|
||||
final PyFunction function = as(getCallable(), PyFunction.class);
|
||||
final boolean isNonStaticMethod = function != null && function.getContainingClass() != null && function.getModifier() != STATICMETHOD;
|
||||
if (isNonStaticMethod) {
|
||||
// In Python 2 unbound methods have __method fake type
|
||||
if (LanguageLevel.forElement(location).isPython2()) {
|
||||
return true;
|
||||
}
|
||||
final PyExpression qualifier;
|
||||
if (location.isQualified()) {
|
||||
qualifier = location.getQualifier();
|
||||
}
|
||||
else {
|
||||
final PyResolveContext resolveContext = PyResolveContext.defaultContext(context);
|
||||
qualifier = ContainerUtil.getLastItem(location.followAssignmentsChain(resolveContext).getQualifiers());
|
||||
}
|
||||
if (qualifier != null) {
|
||||
final PyType qualifierType = PySelfType.extractScopeClassTypeIfNeeded(context.getType(qualifier));
|
||||
if (PyTypeUtil.toStream(qualifierType).select(PyClassType.class).anyMatch(it -> !it.isDefinition())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(location.getProject(), location.getContainingFile());
|
||||
PyExpression callee = location instanceof PyReferenceExpression re ? re.getQualifier() : null;
|
||||
PyClassType delegate = PyUtil.selectCallableTypeRuntimeClass(this, callee, typeEvalContext);
|
||||
return delegate != null ? delegate.getCompletionVariants(completionPrefix, location, context) : ArrayUtilRt.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.Processor;
|
||||
@@ -13,9 +14,8 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public final class PySelfType implements PyTypeParameterType, PyClassLikeType {
|
||||
public final class PySelfType implements PyTypeParameterType, PyClassType {
|
||||
private final @NotNull PyClassType myScopeClassType;
|
||||
private @Nullable PyClass matchingScope = null;
|
||||
|
||||
public PySelfType(@NotNull PyClassType scopeClassType) {
|
||||
myScopeClassType = scopeClassType;
|
||||
@@ -50,13 +50,6 @@ public final class PySelfType implements PyTypeParameterType, PyClassLikeType {
|
||||
return myScopeClassType;
|
||||
}
|
||||
|
||||
public static @Nullable PyType extractScopeClassTypeIfNeeded(@Nullable PyType type) {
|
||||
if (type instanceof PySelfType selfType) {
|
||||
return selfType.myScopeClassType;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if type[Self], false otherwise
|
||||
*/
|
||||
@@ -75,29 +68,6 @@ public final class PySelfType implements PyTypeParameterType, PyClassLikeType {
|
||||
return new PySelfType(myScopeClassType.toClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* The presense of this field indicates that we perform type-check inside the class that this type belongs to.
|
||||
* It means that some special rules should be applied, e.g.:
|
||||
* <pre>
|
||||
* {@code
|
||||
* class Example:
|
||||
* def returns_instance(self) -> Self:
|
||||
* return Example() # Error
|
||||
*
|
||||
* def returns_self(self) -> Self:
|
||||
* return self # OK
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
@Nullable
|
||||
public PyClass getMatchingScope() {
|
||||
return matchingScope;
|
||||
}
|
||||
|
||||
public void setMatchingScope(@Nullable PyClass matchingScopeClass) {
|
||||
this.matchingScope = matchingScopeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@@ -209,6 +179,26 @@ public final class PySelfType implements PyTypeParameterType, PyClassLikeType {
|
||||
return myScopeClassType.getImplicitOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PyClass getPyClass() {
|
||||
return myScopeClassType.getPyClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @Nullable T getUserData(@NotNull Key<T> key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttributeWritable(@NotNull String name, @NotNull TypeEvalContext context) {
|
||||
return myScopeClassType.isAttributeWritable(name, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T acceptTypeVisitor(@NotNull PyTypeVisitor<T> visitor) {
|
||||
if (visitor instanceof PyTypeVisitorExt<T> visitorExt) {
|
||||
|
||||
@@ -143,15 +143,11 @@ public final class PyTypeChecker {
|
||||
}
|
||||
|
||||
if (expected instanceof PySelfType selfType) {
|
||||
return Optional.of(matchSelf(selfType, actual, context));
|
||||
return Optional.of(match(selfType, actual, context));
|
||||
}
|
||||
|
||||
if (actual instanceof PySelfType selfType && context.reversedSubstitutions) {
|
||||
return Optional.of(matchSelf(selfType, expected, context));
|
||||
}
|
||||
|
||||
if (actual instanceof PySelfType selfType) {
|
||||
return Optional.of(matchSelf(expected, selfType, context));
|
||||
return Optional.of(match(selfType, expected, context));
|
||||
}
|
||||
|
||||
if (expected instanceof PyParamSpecType paramSpecType) {
|
||||
@@ -362,59 +358,15 @@ public final class PyTypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean matchSelf(@Nullable PyType expected, @Nullable PyType actual, @NotNull MatchContext context) {
|
||||
if (expected == null || actual == null) {
|
||||
return true;
|
||||
private static boolean match(@NotNull PySelfType expected, @Nullable PyType actual, @NotNull MatchContext context) {
|
||||
if (actual == null) return true;
|
||||
PyType substitution = context.mySubstitutions.getQualifierType();
|
||||
if (substitution != null && !(substitution instanceof PySelfType)) {
|
||||
return match(substitution, actual, context).orElse(false);
|
||||
}
|
||||
if (expected instanceof PySelfType expectedSelf) {
|
||||
var substitutedExpected = substitute(expectedSelf, context.mySubstitutions, context.context);
|
||||
|
||||
if (substitutedExpected instanceof PySelfType expectedSelfAfterSubst) { // no qualifier
|
||||
// The presence of the matching scope indicates that we are within the function Self belongs to
|
||||
if (expectedSelf.getMatchingScope() != null) {
|
||||
if (actual instanceof PySelfType actualSelf) {
|
||||
/*
|
||||
x: Self = cls # Error
|
||||
y: Self = cls() # OK
|
||||
*/
|
||||
return expectedSelf.getMatchingScope().equals(actualSelf.getScopeClassType().getPyClass()) &&
|
||||
expectedSelf.isDefinition() == actualSelf.isDefinition();
|
||||
}
|
||||
/*
|
||||
Other cases should fail, especially important for:
|
||||
class MyClass:
|
||||
def method(self) -> Self:
|
||||
return MyClass() <- Error, forbidden
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
return match(expectedSelfAfterSubst.getScopeClassType(), actual, context).orElse(false);
|
||||
}
|
||||
else { // qualifier is present, match the qualifier and the passed type
|
||||
/*
|
||||
class Z:
|
||||
def method(self): ...
|
||||
|
||||
class A:
|
||||
def method(self):
|
||||
Z.method(self) # E, Self@A
|
||||
Z.method(A()) # E, A
|
||||
*/
|
||||
if (substitutedExpected instanceof PyClassType expectedClassType &&
|
||||
PySelfType.extractScopeClassTypeIfNeeded(actual) instanceof PyClassType actualClassType) {
|
||||
return match(expectedClassType, actualClassType, context).orElse(false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (actual instanceof PySelfType actualSelf) {
|
||||
if (expected instanceof PyClassType expectedClassType) {
|
||||
return match(expectedClassType, actualSelf.getScopeClassType(), context).orElse(false);
|
||||
}
|
||||
}
|
||||
// fallback
|
||||
return match(expected, actual, context).orElse(true);
|
||||
if (!(actual instanceof PySelfType actualSelf)) return false;
|
||||
return expected.isDefinition() == actualSelf.isDefinition() &&
|
||||
match(expected.getScopeClassType(), actualSelf.getScopeClassType(), context).orElse(false);
|
||||
}
|
||||
|
||||
private static @Nullable PyType toClass(@Nullable PyType type) {
|
||||
@@ -664,7 +616,15 @@ public final class PyTypeChecker {
|
||||
private static boolean matchProtocols(@NotNull PyClassType expected, @NotNull PyClassType actual, @NotNull MatchContext matchContext) {
|
||||
GenericSubstitutions substitutions = collectTypeSubstitutions(actual, matchContext.context);
|
||||
|
||||
MatchContext protocolContext = new MatchContext(matchContext.context, new GenericSubstitutions(), matchContext.reversedSubstitutions);
|
||||
|
||||
// See https://typing.python.org/en/latest/spec/generics.html#use-in-protocols
|
||||
// > If a protocol uses Self in methods or attribute annotations, then a class Foo is assignable to the protocol
|
||||
// if its corresponding methods and attribute annotations use either Self or Foo or any of Foo’s subclasses.
|
||||
//
|
||||
// It should be equivalent to replacing Self in the protocol with the Foo class we're matching it with.
|
||||
GenericSubstitutions protocolSubstitutions = new GenericSubstitutions();
|
||||
protocolSubstitutions.qualifierType = actual.toInstance();
|
||||
MatchContext protocolContext = new MatchContext(matchContext.context, protocolSubstitutions, matchContext.reversedSubstitutions);
|
||||
for (kotlin.Pair<PyTypeMember, List<PyTypeMember>> pair : PyProtocolsKt.inspectProtocolSubclass(expected, actual,
|
||||
matchContext.context)) {
|
||||
final PyTypeMember protocolMember = pair.getFirst();
|
||||
@@ -997,9 +957,8 @@ public final class PyTypeChecker {
|
||||
}
|
||||
|
||||
private static @Nullable PyType getActualReturnType(@NotNull PyCallableType actual, @NotNull TypeEvalContext context) {
|
||||
PyCallable callable = actual.getCallable();
|
||||
if (callable instanceof PyFunction) {
|
||||
return getReturnTypeToAnalyzeAsCallType((PyFunction)callable, context);
|
||||
if (actual instanceof PyFunctionType functionType && functionType.getCallable() instanceof PyFunction function) {
|
||||
return getReturnTypeToAnalyzeAsCallType(function, context);
|
||||
}
|
||||
return actual.getReturnType(context);
|
||||
}
|
||||
@@ -1037,8 +996,27 @@ public final class PyTypeChecker {
|
||||
}
|
||||
|
||||
// TODO Make it a part of PyClassType interface
|
||||
|
||||
/**
|
||||
* Extract all type substitutions from a generic class instance. For instance, for the type of `x`
|
||||
*
|
||||
* <pre>{@code
|
||||
* class Base[T1, T2]: ...
|
||||
* class Sub[T3](Base[int, T3]): ...
|
||||
* x: Sub[str]
|
||||
* }</pre>
|
||||
* <p>
|
||||
* we should extract
|
||||
*
|
||||
* <pre>{@code
|
||||
* T1@Base -> int
|
||||
* T2@Base -> Sub@T3
|
||||
* Sub@T3 -> str
|
||||
* }</pre>
|
||||
*/
|
||||
private static @NotNull GenericSubstitutions collectTypeSubstitutions(@NotNull PyClassType classType, @NotNull TypeEvalContext context) {
|
||||
GenericSubstitutions result = new GenericSubstitutions();
|
||||
// Collect substitutions from ancestor classes. In the example above these are: T1@Base -> int and T2@Base -> Sub@T3
|
||||
for (PyTypeProvider provider : PyTypeProvider.EP_NAME.getExtensionList()) {
|
||||
Map<PyType, PyType> substitutionsFromClassDefinition = provider.getGenericSubstitutions(classType.getPyClass(), context);
|
||||
for (Map.Entry<PyType, PyType> entry : substitutionsFromClassDefinition.entrySet()) {
|
||||
@@ -1054,11 +1032,30 @@ public final class PyTypeChecker {
|
||||
result.paramSpecs.put(specType, paramSpecType);
|
||||
}
|
||||
}
|
||||
// Collect own type parameters. In the example above these are: Sub@T3 -> str
|
||||
PyCollectionType genericDefinitionType = as(provider.getGenericType(classType.getPyClass(), context), PyCollectionType.class);
|
||||
// TODO Re-use PyTypeParameterMapping, at the moment C[*Ts] <- C leads to *Ts being mapped to *tuple[], which breaks inference later on
|
||||
if (genericDefinitionType != null) {
|
||||
List<PyType> definitionTypeParameters = genericDefinitionType.getElementTypes();
|
||||
if (!(classType instanceof PyCollectionType genericType)) {
|
||||
|
||||
// Inside method bodies, where Self type can appear, we map class' own type parameters to themseleves,
|
||||
// not to consider them unbound. For instance, here
|
||||
//
|
||||
// class B[T]:...
|
||||
// class C[T2](B[T2]):
|
||||
// attr: T
|
||||
// def m(self):
|
||||
// self.attr
|
||||
//
|
||||
// when collecting substitutions for `self` in `m`, we will map T@B -> T2@C, but T2@C -> T2@C.
|
||||
//
|
||||
// See Py3TypeTest.testApplyingSuperSubstitutionToBoundedGenericClass
|
||||
// and Py3TypeTest#testApplyingSuperSubstitutionToGenericClass
|
||||
if (classType instanceof PySelfType selfType) {
|
||||
mapTypeParametersToSubstitutions(result, definitionTypeParameters, definitionTypeParameters,
|
||||
Option.MAP_UNMATCHED_EXPECTED_TYPES_TO_ANY);
|
||||
}
|
||||
else if (!(classType instanceof PyCollectionType genericType)) {
|
||||
for (PyType typeParameter : definitionTypeParameters) {
|
||||
if (typeParameter instanceof PyTypeVarTupleType typeVarTupleType) {
|
||||
result.typeVarTuples.put(typeVarTupleType, null);
|
||||
@@ -1159,7 +1156,9 @@ public final class PyTypeChecker {
|
||||
}
|
||||
|
||||
public static boolean isUnknown(@Nullable PyType type, boolean genericsAreUnknown, @NotNull TypeEvalContext context) {
|
||||
if (type == null || (genericsAreUnknown && type instanceof PyTypeParameterType)) {
|
||||
// TODO Don't consider other type parameters unknown (PY-85653)
|
||||
// Since Self is always bound, don't consider it unknown, e.g. in Py3TypeCheckerInspectionTest.testSelfAssignedToOtherTypeBad
|
||||
if (type == null || (genericsAreUnknown && type instanceof PyTypeParameterType && !(type instanceof PySelfType))) {
|
||||
return true;
|
||||
}
|
||||
if (type instanceof PyUnionType union) {
|
||||
@@ -1385,6 +1384,17 @@ public final class PyTypeChecker {
|
||||
if (qualifierType == null) {
|
||||
return selfType;
|
||||
}
|
||||
// TODO change unification for calls on union types
|
||||
// so that in the following
|
||||
//
|
||||
// class A:
|
||||
// def a_method(self) -> Self:
|
||||
// ...
|
||||
// x: A | B
|
||||
//
|
||||
// A.a_method # type: Callable[[A], A]
|
||||
// B wasn't considered as the receiver type in the first place, instead of filtering it out during substitution
|
||||
// (see PyTypingTest.testMatchSelfUnionType)
|
||||
return PyTypeUtil.toStream(qualifierType)
|
||||
.map(qType -> {
|
||||
if (qType instanceof PyInstantiableType<?> instantiableType) {
|
||||
@@ -1459,6 +1469,10 @@ public final class PyTypeChecker {
|
||||
parametersSubs instanceof PyParamSpecType paramSpec ? List.of(PyCallableParameterImpl.nonPsi(paramSpec)) :
|
||||
null,
|
||||
clone(callableType.getReturnType(context))
|
||||
,
|
||||
callableType.getCallable(),
|
||||
callableType.getModifier(),
|
||||
callableType.getImplicitOffset()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
import c
|
||||
|
||||
c.A().foo(<warning descr="Parameter(s) unfilledPossible callees:A.foo(self: A, value: None)A.foo(self: A, value: int)A.foo(self: A, value: str)">)</warning>
|
||||
c.A().foo(<warning descr="Parameter(s) unfilledPossible callees:A.foo(self: Self@A, value: None)A.foo(self: Self@A, value: int)A.foo(self: Self@A, value: str)">)</warning>
|
||||
@@ -1,2 +1,2 @@
|
||||
print(dict.fromkeys(<warning descr="Parameter(s) unfilledPossible callees:dict.fromkeys(cls: Type[Dict[_KT, _VT]], __iterable: Iterable[_T])dict.fromkeys(cls: Type[Dict[_KT, _VT]], __iterable: Iterable[_T], __value: _S)">)</warning>)
|
||||
print(dict.fromkeys(<warning descr="Parameter(s) unfilledPossible callees:dict.fromkeys(cls: Type[Self@dict], __iterable: Iterable[_T])dict.fromkeys(cls: Type[Self@dict], __iterable: Iterable[_T], __value: _S)">)</warning>)
|
||||
print(dict.fromkeys(['foo', 'bar']))
|
||||
|
||||
+1
-1
@@ -15,4 +15,4 @@ def f():
|
||||
pass
|
||||
|
||||
|
||||
f().foo<warning descr="Unexpected argument(s)Possible callees:C1.foo(self: C1, x)C2.foo(self: C2, x, y)">(1, 2, 3)</warning>
|
||||
f().foo<warning descr="Unexpected argument(s)Possible callees:C1.foo(self: Self@C1, x)C2.foo(self: Self@C2, x, y)">(1, 2, 3)</warning>
|
||||
|
||||
+1
-1
@@ -15,4 +15,4 @@ def f():
|
||||
pass
|
||||
|
||||
|
||||
f().foo(<warning descr="Parameter(s) unfilledPossible callees:C1.foo(self: C1, x)C2.foo(self: C2, x, y)">)</warning>
|
||||
f().foo(<warning descr="Parameter(s) unfilledPossible callees:C1.foo(self: Self@C1, x)C2.foo(self: Self@C2, x, y)">)</warning>
|
||||
|
||||
+1
-1
@@ -18,4 +18,4 @@ class A:
|
||||
return None
|
||||
|
||||
|
||||
A().foo(<warning descr="Parameter(s) unfilledPossible callees:A.foo(self: A, value: None)A.foo(self: A, value: int)A.foo(self: A, value: str)">)</warning>
|
||||
A().foo(<warning descr="Parameter(s) unfilledPossible callees:A.foo(self: Self@A, value: None)A.foo(self: Self@A, value: int)A.foo(self: Self@A, value: str)">)</warning>
|
||||
@@ -1,5 +1,5 @@
|
||||
print(slice(<warning descr="Parameter(s) unfilledPossible callees:slice(self: slice, stop)slice(self: slice, start, stop, step=...)">)</warning>)
|
||||
print(slice(<warning descr="Parameter(s) unfilledPossible callees:slice(self: Self@slice, stop)slice(self: Self@slice, start, stop, step=...)">)</warning>)
|
||||
print(slice(1))
|
||||
print(slice(1, 2))
|
||||
print(slice(1, 2, 3))
|
||||
print(slice<warning descr="Unexpected argument(s)Possible callees:slice(self: slice, stop)slice(self: slice, start, stop, step=...)">(1, 2, 3, 4)</warning>)
|
||||
print(slice<warning descr="Unexpected argument(s)Possible callees:slice(self: Self@slice, stop)slice(self: Self@slice, start, stop, step=...)">(1, 2, 3, 4)</warning>)
|
||||
|
||||
@@ -2,4 +2,4 @@ print(unicode())
|
||||
print(unicode(''))
|
||||
print(unicode('', 'utf-8'))
|
||||
print(unicode('', 'utf-8', 'ignore'))
|
||||
print(unicode<warning descr="Unexpected argument(s)Possible callees:unicode(self: unicode)unicode(self: unicode, o: object)unicode(self: unicode, o: str, encoding: unicode = ..., errors: unicode = ...)">('', 'utf-8', 'ignore', foo)</warning>)
|
||||
print(unicode<warning descr="Unexpected argument(s)Possible callees:unicode(self: Self@unicode)unicode(self: Self@unicode, o: object)unicode(self: Self@unicode, o: str, encoding: unicode = ..., errors: unicode = ...)">('', 'utf-8', 'ignore', foo)</warning>)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
print(xrange(<warning descr="Parameter(s) unfilledPossible callees:xrange(self: xrange, stop: int)xrange(self: xrange, start: int, stop: int, step: int = ...)">)</warning>)
|
||||
print(xrange(<warning descr="Parameter(s) unfilledPossible callees:xrange(self: Self@xrange, stop: int)xrange(self: Self@xrange, start: int, stop: int, step: int = ...)">)</warning>)
|
||||
print(xrange(1))
|
||||
print(xrange(1, 2))
|
||||
print(xrange(1, 2, 3))
|
||||
print(xrange<warning descr="Unexpected argument(s)Possible callees:xrange(self: xrange, stop: int)xrange(self: xrange, start: int, stop: int, step: int = ...)">(1, 2, 3, 4)</warning>)
|
||||
print(xrange<warning descr="Unexpected argument(s)Possible callees:xrange(self: Self@xrange, stop: int)xrange(self: Self@xrange, start: int, stop: int, step: int = ...)">(1, 2, 3, 4)</warning>)
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ class MyClass(object):
|
||||
|
||||
# Unbound methods are still treated as __method in Python 2
|
||||
MyClass.method.__func__
|
||||
MyClass.method.<warning descr="Cannot find reference '__defaults__' in 'function'">__defaults__</warning>
|
||||
MyClass.method.<warning descr="Cannot find reference '__defaults__' in '(self: MyClass) -> None'">__defaults__</warning>
|
||||
|
||||
# Bound method with qualifier
|
||||
inst = MyClass()
|
||||
|
||||
@@ -3037,7 +3037,7 @@ public class PyTypeTest extends PyTestCase {
|
||||
public void testReplaceDefinitionInMethod() {
|
||||
doTest("Type[Derived]",
|
||||
"""
|
||||
class Base:
|
||||
class Base(object):
|
||||
def cls(self):
|
||||
return self.__class__
|
||||
class Derived(Base):
|
||||
@@ -3046,7 +3046,7 @@ public class PyTypeTest extends PyTestCase {
|
||||
|
||||
doTest("Type[Derived]",
|
||||
"""
|
||||
class Base:
|
||||
class Base(object):
|
||||
def cls(self):
|
||||
return self.__class__
|
||||
class Derived(Base):
|
||||
|
||||
Reference in New Issue
Block a user