mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Unify generic types of qualified reference expressions (PY-16267)
We used to unify generic types in call expressions, including the receivers of calls, but there was no unification of generics for non-callable attributes.
This commit is contained in:
@@ -325,11 +325,33 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyType getTypeFromTarget(@NotNull final PsiElement target,
|
||||
final TypeEvalContext context,
|
||||
PyReferenceExpression anchor) {
|
||||
final PyType type = getGenericTypeFromTarget(target, context, anchor);
|
||||
if (context.maySwitchToAST(anchor)) {
|
||||
final PyExpression qualifier = anchor.getQualifier();
|
||||
if (qualifier != null) {
|
||||
if (!(type instanceof PyFunctionType) && PyTypeChecker.hasGenerics(type, context)) {
|
||||
final Map<PyExpression, PyNamedParameter> parameters = Collections.emptyMap();
|
||||
final Map<PyGenericType, PyType> substitutions = PyTypeChecker.unifyGenericCall(qualifier, parameters, context);
|
||||
if (substitutions != null && !substitutions.isEmpty()) {
|
||||
final PyType substituted = PyTypeChecker.substitute(type, substitutions, context);
|
||||
if (substituted != null) {
|
||||
return substituted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyType getGenericTypeFromTarget(@NotNull final PsiElement target,
|
||||
final TypeEvalContext context,
|
||||
PyReferenceExpression anchor) {
|
||||
if (!(target instanceof PyTargetExpression)) { // PyTargetExpression will ask about its type itself
|
||||
final PyType pyType = getReferenceTypeFromProviders(target, context, anchor);
|
||||
if (pyType != null) {
|
||||
|
||||
@@ -960,6 +960,29 @@ public class PyTypeTest extends PyTestCase {
|
||||
"expr = f\n");
|
||||
}
|
||||
|
||||
// PY-16267
|
||||
public void testGenericField() {
|
||||
doTest("str",
|
||||
"class D(object):\n" +
|
||||
" def __init__(self, foo):\n" +
|
||||
" '''\n" +
|
||||
" :type foo: T\n" +
|
||||
" :rtype: D[T]\n" +
|
||||
" '''\n" +
|
||||
" self.foo = foo\n" +
|
||||
"\n" +
|
||||
"\n" +
|
||||
"def g():\n" +
|
||||
" '''\n" +
|
||||
" :rtype: D[str]\n" +
|
||||
" '''\n" +
|
||||
" return D('test')\n" +
|
||||
"\n" +
|
||||
"\n" +
|
||||
"y = g()\n" +
|
||||
"expr = y.foo\n");
|
||||
}
|
||||
|
||||
private static List<TypeEvalContext> getTypeEvalContexts(@NotNull PyExpression element) {
|
||||
return ImmutableList.of(TypeEvalContext.codeAnalysis(element.getProject(), element.getContainingFile()).withTracing(),
|
||||
TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile()).withTracing());
|
||||
|
||||
@@ -419,6 +419,24 @@ public class PyTypingTest extends PyTestCase {
|
||||
" pass\n");
|
||||
}
|
||||
|
||||
// PY-16267
|
||||
public void testGenericField() {
|
||||
doTest("str",
|
||||
"from typing import TypeVar, Generic\n" +
|
||||
"\n" +
|
||||
"T = TypeVar('T', covariant=True)\n" +
|
||||
"\n" +
|
||||
"class C(Generic[T]):\n" +
|
||||
" def __init__(self, foo: T):\n" +
|
||||
" self.foo = foo\n" +
|
||||
"\n" +
|
||||
"def f() -> C[str]:\n" +
|
||||
" return C('test')\n" +
|
||||
"\n" +
|
||||
"x = f()\n" +
|
||||
"expr = x.foo\n");
|
||||
}
|
||||
|
||||
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