PY-22730 Don't render Optional[Any] as Union[None, Any]

Specifically, handle "weak" union type only after checking that a
union can be displayed as Optional.
This commit is contained in:
Mikhail Golubev
2017-03-06 15:20:44 +03:00
parent d5cbfa6950
commit fb845a9ba2
6 changed files with 31 additions and 13 deletions
@@ -17,6 +17,7 @@ package com.jetbrains.python.documentation;
import com.google.common.collect.Collections2;
import com.google.common.collect.Maps;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
@@ -268,7 +269,11 @@ public class PyTypeModelBuilder {
else if (type instanceof PyUnionType && allowUnions) {
final PyUnionType unionType = (PyUnionType)type;
final Collection<PyType> unionMembers = unionType.getMembers();
if (type instanceof PyDynamicallyEvaluatedType || PyTypeChecker.isUnknown(type, false)) {
final Ref<PyType> optionalType = getOptionalType(unionType);
if (optionalType != null) {
result = new OptionalType(build(optionalType.get(), true));
}
else if (type instanceof PyDynamicallyEvaluatedType || PyTypeChecker.isUnknown(type, false)) {
result = new UnknownType(build(unionType.excludeNull(myContext), true));
}
else if (unionMembers.stream().allMatch(t -> t instanceof PyClassType && ((PyClassType)t).isDefinition())) {
@@ -276,10 +281,7 @@ public class PyTypeModelBuilder {
result = new ClassObjectType(new OneOf(instanceTypes));
}
else {
result = Optional
.ofNullable(getOptionalType(unionType))
.<TypeModel>map(optionalType -> new OptionalType(build(optionalType, true)))
.orElseGet(() -> new OneOf(Collections2.transform(unionMembers, t -> build(t, false))));
result = new OneOf(Collections2.transform(unionMembers, t -> build(t, false)));
}
}
else if (type instanceof PyCallableType && !(type instanceof PyClassLikeType)) {
@@ -318,7 +320,7 @@ public class PyTypeModelBuilder {
}
@Nullable
private static PyType getOptionalType(@NotNull PyUnionType type) {
private static Ref<PyType> getOptionalType(@NotNull PyUnionType type) {
final Collection<PyType> members = type.getMembers();
if (members.size() == 2) {
boolean foundNone = false;
@@ -332,7 +334,7 @@ public class PyTypeModelBuilder {
}
}
if (foundNone) {
return optional;
return Ref.create(optional);
}
}
return null;
@@ -31,6 +31,6 @@ def expects_myclass_descendant_or_none(x: Optional[Type[T2]]):
pass
expects_myclass_descendant_or_none(MyClass)
expects_myclass_descendant_or_none(<weak_warning descr="Expected type 'Union[None, Any]' (matched generic type 'Optional[Type[TypeVar('T2', MyClass)]]'), got 'MyClass' instead">MyClass()</weak_warning>)
expects_myclass_descendant_or_none(<weak_warning descr="Expected type 'Union[None, Any]' (matched generic type 'Optional[Type[TypeVar('T2', MyClass)]]'), got 'Type[object]' instead">object</weak_warning>)
expects_myclass_descendant_or_none(<weak_warning descr="Expected type 'Union[None, Any]' (matched generic type 'Optional[Type[TypeVar('T2', MyClass)]]'), got 'object' instead">object()</weak_warning>)
expects_myclass_descendant_or_none(<weak_warning descr="Expected type 'Optional[Any]' (matched generic type 'Optional[Type[TypeVar('T2', MyClass)]]'), got 'MyClass' instead">MyClass()</weak_warning>)
expects_myclass_descendant_or_none(<weak_warning descr="Expected type 'Optional[Any]' (matched generic type 'Optional[Type[TypeVar('T2', MyClass)]]'), got 'Type[object]' instead">object</weak_warning>)
expects_myclass_descendant_or_none(<weak_warning descr="Expected type 'Optional[Any]' (matched generic type 'Optional[Type[TypeVar('T2', MyClass)]]'), got 'object' instead">object()</weak_warning>)
@@ -0,0 +1,11 @@
from typing import Optional, TypeVar
T = TypeVar('T', int)
def expects_int_subclass_or_none(x: Optional[T]):
pass
expects_int_subclass_or_none(<weak_warning descr="Expected type 'Optional[Any]' (matched generic type 'Optional[TypeVar('T', int)]'), got 'str' instead">'foo'</weak_warning>)
@@ -450,7 +450,7 @@ public class Py3TypeTest extends PyTestCase {
// PY-20757
public void testMinElseNone() {
doTest("Union[None, Any]",
doTest("Optional[Any]",
"def get_value(v):\n" +
" if v:\n" +
" return min(v)\n" +
@@ -1496,14 +1496,14 @@ public class PyTypeTest extends PyTestCase {
// PY-20409
public void testGetFromDictWithDefaultNoneValue() {
doTest("Union[None, Any]",
doTest("Optional[Any]",
"d = {}\n" +
"expr = d.get(\"abc\", None)");
}
// PY-20757
public void testMinOrNone() {
doTest("Union[None, Any]",
doTest("Optional[Any]",
"def get_value(v):\n" +
" if v:\n" +
" return min(v)\n" +
@@ -213,4 +213,9 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase {
public void testClassObjectTypeWithUnion() {
doTest();
}
// PY-22730
public void testOptionalOfBoundTypeVarInWarnings() {
doTest();
}
}