mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Provide special type for typing.TypeVar as a callee (PY-28127)
This commit is contained in:
@@ -86,9 +86,6 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
private static final String CHAIN_MAP = "typing.ChainMap";
|
||||
private static final String UNION = "typing.Union";
|
||||
private static final String OPTIONAL = "typing.Optional";
|
||||
private static final String CLASSVAR = "typing.ClassVar";
|
||||
|
||||
public static final String NAMEDTUPLE_SIMPLE = "NamedTuple";
|
||||
|
||||
private static final String PY2_FILE_TYPE = "typing.BinaryIO";
|
||||
private static final String PY3_BINARY_FILE_TYPE = "typing.BinaryIO";
|
||||
@@ -176,7 +173,12 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
}
|
||||
}
|
||||
|
||||
return getNewTypeForReference(referenceExpression, context);
|
||||
final PyType newType = getNewTypeForReference(referenceExpression, context);
|
||||
if (newType != null) {
|
||||
return newType;
|
||||
}
|
||||
|
||||
return getTypeVarTypeForCallee(referenceExpression, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -295,6 +297,32 @@ public class PyTypingTypeProvider extends PyTypeProviderBase {
|
||||
return new PyCustomType(CALLABLE, null, false, true, PyBuiltinCache.getInstance(anchor).getObjectType());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyType getTypeVarTypeForCallee(@NotNull PyReferenceExpression referenceExpression, @NotNull TypeEvalContext context) {
|
||||
if (PyCallExpressionNavigator.getPyCallExpressionByCallee(referenceExpression) == null) return null;
|
||||
|
||||
if (resolveToQualifiedNames(referenceExpression, context).contains(TYPE_VAR)) {
|
||||
final List<PyCallableParameter> parameters = new ArrayList<>();
|
||||
|
||||
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(referenceExpression);
|
||||
final LanguageLevel languageLevel = LanguageLevel.forElement(referenceExpression);
|
||||
final PyElementGenerator generator = PyElementGenerator.getInstance(referenceExpression.getProject());
|
||||
|
||||
parameters.add(PyCallableParameterImpl.nonPsi("name", builtinCache.getStringType(languageLevel)));
|
||||
parameters.add(PyCallableParameterImpl.positionalNonPsi("constraints", builtinCache.getTypeType()));
|
||||
parameters.add(PyCallableParameterImpl.nonPsi("bound", builtinCache.getTypeType(), generator.createEllipsis()));
|
||||
|
||||
final PyClassType boolType = builtinCache.getBoolType();
|
||||
final PyExpression falseValue = generator.createExpressionFromText(languageLevel, "False");
|
||||
parameters.add(PyCallableParameterImpl.nonPsi("covariant", boolType, falseValue));
|
||||
parameters.add(PyCallableParameterImpl.nonPsi("contravariant", boolType, falseValue));
|
||||
|
||||
return new PyCallableTypeImpl(parameters, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean omitFirstParamInTypeComment(@NotNull PyFunction func, @NotNull PyFunctionTypeAnnotation annotation) {
|
||||
return func.getContainingClass() != null && func.getModifier() != PyFunction.Modifier.STATICMETHOD &&
|
||||
annotation.getParameterTypeList().getParameterTypes().size() < func.getParameterList().getParameters().length;
|
||||
|
||||
@@ -35,15 +35,21 @@ public class PyCallableParameterImpl implements PyCallableParameter {
|
||||
@Nullable private final Ref<PyType> myType;
|
||||
@Nullable private final PyExpression myDefaultValue;
|
||||
@Nullable private final PyParameter myElement;
|
||||
private final boolean myIsPositional;
|
||||
private final boolean myIsKeyword;
|
||||
|
||||
private PyCallableParameterImpl(@Nullable String name,
|
||||
@Nullable Ref<PyType> type,
|
||||
@Nullable PyExpression defaultValue,
|
||||
@Nullable PyParameter element) {
|
||||
@Nullable PyParameter element,
|
||||
boolean isPositional,
|
||||
boolean isKeyword) {
|
||||
myName = name;
|
||||
myType = type;
|
||||
myDefaultValue = defaultValue;
|
||||
myElement = element;
|
||||
myIsPositional = isPositional;
|
||||
myIsKeyword = isKeyword;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -58,17 +64,27 @@ public class PyCallableParameterImpl implements PyCallableParameter {
|
||||
|
||||
@NotNull
|
||||
public static PyCallableParameter nonPsi(@Nullable String name, @Nullable PyType type, @Nullable PyExpression defaultValue) {
|
||||
return new PyCallableParameterImpl(name, Ref.create(type), defaultValue, null);
|
||||
return new PyCallableParameterImpl(name, Ref.create(type), defaultValue, null, false, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PyCallableParameter positionalNonPsi(@Nullable String name, @Nullable PyType type) {
|
||||
return new PyCallableParameterImpl(name, Ref.create(type), null, null, true, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PyCallableParameter keywordNonPsi(@Nullable String name, @Nullable PyType type) {
|
||||
return new PyCallableParameterImpl(name, Ref.create(type), null, null, false, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PyCallableParameter psi(@NotNull PyParameter parameter) {
|
||||
return new PyCallableParameterImpl(null, null, null, parameter);
|
||||
return new PyCallableParameterImpl(null, null, null, parameter, false, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PyCallableParameter psi(@NotNull PyParameter parameter, @Nullable PyType type) {
|
||||
return new PyCallableParameterImpl(null, Ref.create(type), null, parameter);
|
||||
return new PyCallableParameterImpl(null, Ref.create(type), null, parameter, false, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -121,12 +137,16 @@ public class PyCallableParameterImpl implements PyCallableParameter {
|
||||
|
||||
@Override
|
||||
public boolean isPositionalContainer() {
|
||||
if (myIsPositional) return true;
|
||||
|
||||
final PyNamedParameter namedParameter = PyUtil.as(myElement, PyNamedParameter.class);
|
||||
return namedParameter != null && namedParameter.isPositionalContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeywordContainer() {
|
||||
if (myIsKeyword) return true;
|
||||
|
||||
final PyNamedParameter namedParameter = PyUtil.as(myElement, PyNamedParameter.class);
|
||||
return namedParameter != null && namedParameter.isKeywordContainer();
|
||||
}
|
||||
@@ -210,13 +230,16 @@ public class PyCallableParameterImpl implements PyCallableParameter {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final PyCallableParameterImpl parameter = (PyCallableParameterImpl)o;
|
||||
return Objects.equals(myName, parameter.myName) &&
|
||||
return myIsPositional == parameter.myIsPositional &&
|
||||
myIsKeyword == parameter.myIsKeyword &&
|
||||
Objects.equals(myName, parameter.myName) &&
|
||||
Objects.equals(Ref.deref(myType), Ref.deref(parameter.myType)) &&
|
||||
Objects.equals(myDefaultValue, parameter.myDefaultValue) &&
|
||||
Objects.equals(myElement, parameter.myElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(myName, Ref.deref(myType), myElement);
|
||||
return Objects.hash(myName, Ref.deref(myType), myDefaultValue, myElement, myIsPositional, myIsKeyword);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
from typing import TypeVar
|
||||
|
||||
TypeVar("T")
|
||||
TypeVar("T", int)
|
||||
TypeVar("T", int, str)
|
||||
|
||||
TypeVar("T", bound=int)
|
||||
TypeVar("T", int, bound=int)
|
||||
TypeVar("T", int, str, bound=int)
|
||||
|
||||
TypeVar("T", <warning descr="Unexpected argument">bd=int</warning>)
|
||||
TypeVar("T", int, <warning descr="Unexpected argument">bd=int</warning>)
|
||||
TypeVar("T", int, str, <warning descr="Unexpected argument">bd=int</warning>)
|
||||
|
||||
TypeVar("T", bound=int, covariant=True)
|
||||
TypeVar("T", int, bound=int, covariant=True)
|
||||
TypeVar("T", int, str, bound=int, covariant=True)
|
||||
|
||||
TypeVar("T", bound=int, <warning descr="Unexpected argument">cant=True</warning>)
|
||||
TypeVar("T", int, bound=int, <warning descr="Unexpected argument">cant=True</warning>)
|
||||
TypeVar("T", int, str, bound=int, <warning descr="Unexpected argument">cant=True</warning>)
|
||||
|
||||
TypeVar("T", bound=int, covariant=True, contravariant=True)
|
||||
TypeVar("T", int, bound=int, covariant=True, contravariant=True)
|
||||
TypeVar("T", int, str, bound=int, covariant=True, contravariant=True)
|
||||
|
||||
TypeVar("T", bound=int, covariant=True, <warning descr="Unexpected argument">cant=True</warning>)
|
||||
TypeVar("T", int, bound=int, covariant=True, <warning descr="Unexpected argument">cant=True</warning>)
|
||||
TypeVar("T", int, str, bound=int, covariant=True, <warning descr="Unexpected argument">cant=True</warning>)
|
||||
|
||||
TypeVar("T", bound=int, covariant=True, contravariant=True, <warning descr="Unexpected argument">more=5</warning>)
|
||||
TypeVar("T", int, bound=int, covariant=True, contravariant=True, <warning descr="Unexpected argument">more=5</warning>)
|
||||
TypeVar("T", int, str, bound=int, covariant=True, contravariant=True, <warning descr="Unexpected argument">more=5</warning>)
|
||||
@@ -0,0 +1,4 @@
|
||||
from typing import TypeVar
|
||||
|
||||
TypeVar("T", int, str, bound=int, covariant=True, contravariant=True)
|
||||
TypeVar("T", <warning descr="Expected type 'type', got 'int' instead">0</warning>, <warning descr="Expected type 'type', got 'int' instead">1</warning>, <warning descr="Expected type 'type', got 'int' instead">bound=2</warning>, <warning descr="Expected type 'bool', got 'int' instead">covariant=3</warning>, <warning descr="Expected type 'bool', got 'int' instead">contravariant=4</warning>)
|
||||
@@ -0,0 +1,3 @@
|
||||
from typing import TypeVar
|
||||
|
||||
T = TypeVar(<arg1>)
|
||||
@@ -782,6 +782,20 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
);
|
||||
}
|
||||
|
||||
// PY-28127
|
||||
public void testInitializingTypeVar() {
|
||||
runWithLanguageLevel(
|
||||
LanguageLevel.PYTHON34,
|
||||
() -> {
|
||||
final int offset = loadTest(1).get("<arg1>").getTextOffset();
|
||||
|
||||
feignCtrlP(offset).check("name: str, *constraints: type, bound: type=..., covariant: bool=False, contravariant: bool=False",
|
||||
new String[]{"name: str, "},
|
||||
ArrayUtil.EMPTY_STRING_ARRAY);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imitates pressing of Ctrl+P; fails if results are not as expected.
|
||||
* @param offset offset of 'cursor' where Ctrl+P is pressed.
|
||||
|
||||
@@ -301,4 +301,9 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase {
|
||||
public void testDataclassesReplace() {
|
||||
runWithLanguageLevel(LanguageLevel.PYTHON37, () -> super.doMultiFileTest());
|
||||
}
|
||||
|
||||
// PY-28127
|
||||
public void testInitializingTypeVar() {
|
||||
doTest();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,4 +347,9 @@ public class PyArgumentListInspectionTest extends PyInspectionTestCase {
|
||||
public void testObjectMethodInPossiblyInheritanceChain() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-28127
|
||||
public void testInitializingTypeVar() {
|
||||
doTest();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user