Show generic types in PEP 484 notation (PY-16889)

In particular, show unresolved refernces in generic bounds as Any.
We used to ignore these unresolved types which resulted in
incomprehensible warning messages.
This commit is contained in:
Andrey Vlasovskikh
2015-10-02 21:50:55 +03:00
parent f533f02126
commit 955e588e03
10 changed files with 42 additions and 11 deletions
@@ -15,8 +15,11 @@
*/
package com.jetbrains.python.psi.types;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.util.Function;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.AccessDirection;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.resolve.PyResolveContext;
@@ -55,7 +58,19 @@ public class PyGenericType implements PyType {
@NotNull
@Override
public String getName() {
return myBound != null ? myName + " <= " + myBound.getName() : myName;
if (myBound instanceof PyUnionType) {
final PyUnionType bounds = (PyUnionType)myBound;
final String boundsString = StringUtil.join(bounds.getMembers(), new Function<PyType, String>() {
@Override
public String fun(PyType type) {
return type != null ? type.getName() : PyNames.UNKNOWN_TYPE;
}
}, ", ");
return "TypeVar('" + myName + "', " + boundsString + ")";
}
else {
return "TypeVar('" + myName + "')";
}
}
@Override
@@ -8,5 +8,5 @@ def test():
x = f(10)
y = f('foo')
z = f(<warning descr="Expected type 'T <= int | str', got 'list' instead">[]</warning>)
z = f(<warning descr="Expected type 'TypeVar('T', int, str)', got 'list' instead">[]</warning>)
return x + <warning descr="Expected type 'Number', got 'str' instead">y</warning>
@@ -21,4 +21,4 @@ class User1(object):
c = User1(10)
print(c.get() + <warning descr="Expected type 'Number', got 'str' instead">'foo'</warning>)
c.put(14)
c.put(<weak_warning descr="Expected type 'int' (matched generic type 'T'), got 'str' instead">'foo'</weak_warning>)
c.put(<weak_warning descr="Expected type 'int' (matched generic type 'TypeVar('T')'), got 'str' instead">'foo'</weak_warning>)
@@ -40,7 +40,7 @@ def test():
print(result)
print(result + <warning descr="Expected type 'Number', got 'str' instead">'foo'</warning>)
f2(1, <weak_warning descr="Expected type 'list[int]' (matched generic type 'list[T]'), got 'list[str]' instead">['foo']</weak_warning>, 'bar')
f2(1, <weak_warning descr="Expected type 'list[int]' (matched generic type 'list[TypeVar('T')]'), got 'list[str]' instead">['foo']</weak_warning>, 'bar')
result = f3(1, 'foo', True)
f4(<warning descr="Expected type 'Tuple[bool, int, str]', got 'Tuple[int, str, bool]' instead">result</warning>)
@@ -7,7 +7,7 @@ def test_second_form():
def test_second_form_fail():
for chunk in iter(<weak_warning descr="Expected type 'Union[Iterable, () -> object]' (matched generic type 'Union[Iterable[T], () -> object]'), got 'int' instead">10</weak_warning>, ''):
for chunk in iter(<weak_warning descr="Expected type 'Union[Iterable, () -> object]' (matched generic type 'Union[Iterable[TypeVar('T')], () -> object]'), got 'int' instead">10</weak_warning>, ''):
pass
@@ -0,0 +1,11 @@
from typing import TypeVar
T = TypeVar('T', int, unresolved)
def calc(a: T, b: T):
pass
calc('a', <weak_warning descr="Expected type 'str' (matched generic type 'TypeVar('T', int, Any)'), got 'int' instead">0</weak_warning>)
@@ -31,6 +31,6 @@ def test_stub_only_function(x):
def tset_overloaded_generics(x):
g(<warning descr="Expected type 'dict', got 'int' instead">Gen(10).get(10, 10)</warning>)
g(Gen(10).get(10, <weak_warning descr="Expected type 'int' (matched generic type 'T'), got 'str' instead">'foo'</weak_warning>))
g(Gen('foo').get(10, <weak_warning descr="Expected type 'str' (matched generic type 'T'), got 'int' instead">10</weak_warning>))
g(Gen(10).get(10, <weak_warning descr="Expected type 'int' (matched generic type 'TypeVar('T')'), got 'str' instead">'foo'</weak_warning>))
g(Gen('foo').get(10, <weak_warning descr="Expected type 'str' (matched generic type 'TypeVar('T')'), got 'int' instead">10</weak_warning>))
g(<warning descr="Expected type 'dict', got 'str' instead">Gen('foo').get(10, 'foo')</warning>)
@@ -137,7 +137,7 @@ public class PyTypeParserTest extends PyTestCase {
final PyType type = PyTypeParser.getTypeByName(myFixture.getFile(), "T");
assertNotNull(type);
assertInstanceOf(type, PyGenericType.class);
assertEquals("T", type.getName());
assertEquals("TypeVar('T')", type.getName());
}
// PY-4223
@@ -251,7 +251,7 @@ public class PyTypeParserTest extends PyTestCase {
assertEquals("int", type0.getName());
final PyType type1 = parameterTypes.get(1).getType(context);
assertNotNull(type1);
assertEquals("T", type1.getName());
assertEquals("TypeVar('T')", type1.getName());
}
public void testCallableWithoutArgs() {
@@ -151,7 +151,7 @@ public class PyTypingTest extends PyTestCase {
}
public void testGenericType() {
doTest("A",
doTest("TypeVar('A')",
"from typing import TypeVar\n" +
"\n" +
"T = TypeVar('A')\n" +
@@ -161,7 +161,7 @@ public class PyTypingTest extends PyTestCase {
}
public void testGenericBoundedType() {
doTest("T <= int | str",
doTest("TypeVar('T', int, str)",
"from typing import TypeVar\n" +
"\n" +
"T = TypeVar('T', int, str)\n" +
@@ -78,4 +78,9 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase {
public void testTypingListSubscriptionExpression() {
doTest();
}
// PY-16855
public void testTypingTypeVarWithUnresolvedBound() {
doTest();
}
}