mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-20057 Unify type vars constrained by class types and instance types
so that annotations like "def f(x: Type[T]) -> T" (and vice-versa) work as expected. Additionally, I've added a new method toClass() in PyInstantiableType interface that is supposed to be the opposite to existing toInstance().
This commit is contained in:
@@ -128,11 +128,18 @@ public class PyJavaClassType implements PyClassLikeType {
|
||||
return myDefinition;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toInstance() {
|
||||
return myDefinition ? new PyJavaClassType(myClass, false) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toClass() {
|
||||
return myDefinition ? this : new PyJavaClassType(myClass, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getClassQName() {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A type that can describe either particular instantiated instance of a class or a class itself.
|
||||
*
|
||||
@@ -23,5 +25,9 @@ package com.jetbrains.python.psi.types;
|
||||
public interface PyInstantiableType<T extends PyInstantiableType<T>> extends PyType {
|
||||
boolean isDefinition();
|
||||
|
||||
@NotNull
|
||||
T toInstance();
|
||||
|
||||
@NotNull
|
||||
T toClass();
|
||||
}
|
||||
|
||||
@@ -84,14 +84,23 @@ public class PyCustomType implements PyClassLikeType {
|
||||
return !myInstanceType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public final PyClassLikeType toInstance() {
|
||||
return myInstanceType
|
||||
? this
|
||||
: new PyCustomType(myQualifiedName, myFilter, true, myTypesToMimic.toArray(new PyClassLikeType[myTypesToMimic.size()]));
|
||||
: new PyCustomType(myQualifiedName, myFilter, true, myTypesToMimic.toArray(new PyClassLikeType[0]));
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toClass() {
|
||||
return myInstanceType
|
||||
? new PyCustomType(myQualifiedName, myFilter, false, myTypesToMimic.toArray(new PyClassLikeType[0]))
|
||||
: this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final String getClassQName() {
|
||||
|
||||
@@ -103,11 +103,18 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassType toInstance() {
|
||||
return myDefinitionLevel == 1 ? new PyNamedTupleType(myClass, myDeclaration, myName, myFields, 0) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toClass() {
|
||||
return myDefinitionLevel == 0 ? this : new PyNamedTupleType(myClass, myDeclaration, myName, myFields, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PyNamedTupleType: " + myName;
|
||||
|
||||
@@ -107,11 +107,18 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
return myIsDefinition;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassType toInstance() {
|
||||
return myIsDefinition ? withUserDataCopy(new PyClassTypeImpl(myClass, false)) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toClass() {
|
||||
return myIsDefinition ? this : new PyClassTypeImpl(myClass, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap new instance to copy user data to it
|
||||
*/
|
||||
|
||||
@@ -70,11 +70,18 @@ public class PyCollectionTypeImpl extends PyClassTypeImpl implements PyCollectio
|
||||
return new PyCollectionTypeImpl(pyClass, isDefinition, elementTypes);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassType toInstance() {
|
||||
return myIsDefinition ? withUserDataCopy(new PyCollectionTypeImpl(myClass, false, myElementTypes)) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyClassLikeType toClass() {
|
||||
return myIsDefinition ? this : withUserDataCopy(new PyCollectionTypeImpl(myClass, true, myElementTypes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@@ -108,8 +108,15 @@ public class PyGenericType implements PyType, PyInstantiableType<PyGenericType>
|
||||
return myIsDefinition;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyGenericType toInstance() {
|
||||
return myIsDefinition ? new PyGenericType(myName, myBound, false) : this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PyGenericType toClass() {
|
||||
return myIsDefinition ? this : new PyGenericType(myName, myBound, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +416,22 @@ public class PyTypeChecker {
|
||||
@NotNull TypeEvalContext context) {
|
||||
if (hasGenerics(type, context)) {
|
||||
if (type instanceof PyGenericType) {
|
||||
final PyType substitution = substitutions.get((PyGenericType)type);
|
||||
final PyGenericType typeVar = (PyGenericType)type;
|
||||
PyType substitution = substitutions.get(typeVar);
|
||||
if (substitution == null) {
|
||||
if (!typeVar.isDefinition()) {
|
||||
final PyInstantiableType<?> classType = as(substitutions.get(typeVar.toClass()), PyInstantiableType.class);
|
||||
if (classType != null) {
|
||||
substitution = classType.toInstance();
|
||||
}
|
||||
}
|
||||
else {
|
||||
final PyInstantiableType<?> instanceType = as(substitutions.get(typeVar.toInstance()), PyInstantiableType.class);
|
||||
if (instanceType != null) {
|
||||
substitution = instanceType.toClass();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (substitution instanceof PyGenericType && substitution != type) {
|
||||
final PyType recursive = substitute(substitution, substitutions, context);
|
||||
if (recursive != null) {
|
||||
|
||||
@@ -807,11 +807,11 @@ public class PyTypingTest extends PyTestCase {
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testConstrainedClassObjectTypeOfResultValue() {
|
||||
public void testFunctionCreatesInstanceFromType() {
|
||||
doTest("int",
|
||||
"from typing import Type, TypeVar\n" +
|
||||
"\n" +
|
||||
"T = TypeVar('T', bound=int)\n" +
|
||||
"T = TypeVar('T')\n" +
|
||||
"\n" +
|
||||
"def f(x: Type[T]) -> T:\n" +
|
||||
" return x()\n" +
|
||||
@@ -819,6 +819,19 @@ public class PyTypingTest extends PyTestCase {
|
||||
"expr = f(int)");
|
||||
}
|
||||
|
||||
// PY-20057
|
||||
public void testFunctionReturnsTypeOfInstance() {
|
||||
doTest("Type[int]",
|
||||
"from typing import Type, TypeVar\n" +
|
||||
"\n" +
|
||||
"T = TypeVar('T')\n" +
|
||||
"\n" +
|
||||
"def f(x: T) -> Type[T]:\n" +
|
||||
" return type(T)\n" +
|
||||
" \n" +
|
||||
"expr = f(42)");
|
||||
}
|
||||
|
||||
private void doTestNoInjectedText(@NotNull String text) {
|
||||
myFixture.configureByText(PythonFileType.INSTANCE, text);
|
||||
final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myFixture.getProject());
|
||||
|
||||
Reference in New Issue
Block a user