mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Simplified matching instances and definitions (PY-38786)
The problem was that every definition matches `TypeVar(..., bound=type)` since it's metaclass is `type`.
The metaclass successfully matches expected class but no substitution is created.
Tests were updated since now `A` matches `_C = TypeVar("_C", bound=type)` in attrs stubs and `A` is inferred.
In any case `attrs` support is tested in other tests, these tests are about unknown (for us) decorators.
GitOrigin-RevId: ced931bd2ff732a8f7a95726d3f2837417d2d6c2
This commit is contained in:
committed by
intellij-monorepo-bot
parent
17d5a4bbce
commit
04a502ed31
@@ -97,13 +97,6 @@ public class PyTypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
if (expected instanceof PyInstantiableType && actual instanceof PyInstantiableType) {
|
||||
Optional<Boolean> match = match((PyInstantiableType)expected, (PyInstantiableType)actual, context);
|
||||
if (match.isPresent()) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
if (expected instanceof PyGenericType) {
|
||||
return Optional.of(match((PyGenericType)expected, actual, context));
|
||||
}
|
||||
@@ -181,25 +174,6 @@ public class PyTypeChecker {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Optional<Boolean> match(@NotNull PyInstantiableType expected, @NotNull PyInstantiableType actual,
|
||||
@NotNull MatchContext context) {
|
||||
if (expected instanceof PyGenericType && typeVarAcceptsBothClassAndInstanceTypes((PyGenericType)expected)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (expected.isDefinition() ^ actual.isDefinition()) {
|
||||
if (actual.isDefinition() &&
|
||||
actual instanceof PyClassLikeType &&
|
||||
matchClassObjectAndMetaclass(expected, (PyClassLikeType)actual, context)) {
|
||||
return Optional.of(true);
|
||||
}
|
||||
return Optional.of(false);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Match {@code actual} versus {@code PyGenericType expected}.
|
||||
*
|
||||
@@ -234,6 +208,9 @@ public class PyTypeChecker {
|
||||
}
|
||||
|
||||
if (actual != null) {
|
||||
if (expected.isDefinition() && !(actual instanceof PyInstantiableType && ((PyInstantiableType)actual).isDefinition())) {
|
||||
return false;
|
||||
}
|
||||
context.substitutions.put(expected, actual);
|
||||
}
|
||||
else if (bound != null) {
|
||||
@@ -275,6 +252,14 @@ public class PyTypeChecker {
|
||||
return Optional.of(true);
|
||||
}
|
||||
|
||||
if (expected.isDefinition() ^ actual.isDefinition()) {
|
||||
if (!expected.isDefinition() && actual.isDefinition()) {
|
||||
final PyClassLikeType metaClass = actual.getMetaClassType(context.context, true);
|
||||
return Optional.of(metaClass != null && match((PyType)expected, metaClass.toInstance(), context).orElse(true));
|
||||
}
|
||||
return Optional.of(false);
|
||||
}
|
||||
|
||||
if (expected instanceof PyTupleType && actual instanceof PyTupleType) {
|
||||
return match((PyTupleType)expected, (PyTupleType)actual, context);
|
||||
}
|
||||
@@ -488,21 +473,6 @@ public class PyTypeChecker {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static boolean matchClassObjectAndMetaclass(@NotNull PyType expected,
|
||||
@NotNull PyClassLikeType actual,
|
||||
@NotNull MatchContext context) {
|
||||
|
||||
if (!actual.isDefinition()) {
|
||||
return false;
|
||||
}
|
||||
final PyClassLikeType metaClass = actual.getMetaClassType(context.context, true);
|
||||
return metaClass != null && match(expected, metaClass.toInstance(), context).orElse(true);
|
||||
}
|
||||
|
||||
private static boolean typeVarAcceptsBothClassAndInstanceTypes(@NotNull PyGenericType typeVar) {
|
||||
return !typeVar.isDefinition() && typeVar.getBound() == null;
|
||||
}
|
||||
|
||||
private static boolean consistsOfSameElementNumberTuples(@NotNull PyUnionType unionType, int elementCount) {
|
||||
for (PyType type : unionType.getMembers()) {
|
||||
if (type instanceof PyTupleType) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import attr
|
||||
import abcd
|
||||
|
||||
class A(object):
|
||||
a = attr.ib()
|
||||
a = abcd.ib()
|
||||
|
||||
A = attr.s(A)
|
||||
A = abcd.s(A)
|
||||
|
||||
A(a="test")
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import attr
|
||||
import abcd
|
||||
|
||||
@attr.s
|
||||
@abcd.s
|
||||
class A(object):
|
||||
a = attr.ib()
|
||||
a = abcd.ib()
|
||||
|
||||
A(a="test")
|
||||
@@ -3877,6 +3877,33 @@ public class PyTypeTest extends PyTestCase {
|
||||
"expr = example.__doc__");
|
||||
}
|
||||
|
||||
// PY-38786
|
||||
public void testParticularTypeAgainstTypeVarBoundedWithBuiltinType() {
|
||||
runWithLanguageLevel(
|
||||
LanguageLevel.getLatest(),
|
||||
() -> doTest("Type[MyClass]",
|
||||
"from typing import TypeVar, Type\n" +
|
||||
"\n" +
|
||||
"T = TypeVar(\"T\", bound=type)\n" +
|
||||
"\n" +
|
||||
"def foo(t: T) -> T:\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"class MyClass:\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"expr = foo(MyClass)")
|
||||
);
|
||||
}
|
||||
|
||||
// PY-38786
|
||||
public void testDunderSubclasses() {
|
||||
doTest("List[Type[Base]]",
|
||||
"class Base(object):\n" +
|
||||
" pass\n" +
|
||||
"expr = Base.__subclasses__()");
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
Reference in New Issue
Block a user