From 030853a3e23654e914e077a7835baef0a32bbc05 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 15 Feb 2016 15:03:08 +0300 Subject: [PATCH] PY-18254 IDEA-CR-8433 Represent types of varargs annotated with **T or *T as dicts and raw tuples Type of vararg parameter should be seen differently inside method body and at the call site. For instance, in case of positional vararg, it should be represented as "Iterable[T]" in function quick doc, but as "Tuple[T, ...]" inside its body, because without the latter type checker might complain about missing tuple's methods called on such parameter. Unfortunately, we're not able to represent homogeneous tuples of arbitrary length in our type system so far, so it's just raw tuple for now. The same thing applies to keyword varargs, since collections.Mapping doesn't contain all methods defined for built-in dict. At least we can parametrize type of the standard dict properly with the types of its keys and values. --- .../python/psi/types/PyTypeParser.java | 18 +++++++++--------- .../varargsAnnotatedWithFunctionComment.py | 4 ++++ .../com/jetbrains/python/PyTypeParserTest.java | 8 +++----- .../com/jetbrains/python/PyTypingTest.java | 2 +- .../PyUnresolvedReferencesInspectionTest.java | 5 +++++ 5 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/varargsAnnotatedWithFunctionComment.py diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java index bcacc1a48de8..f092da995b91 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java @@ -403,20 +403,20 @@ public class PyTypeParser { return paramResult; } else if (starCount == 1) { - final PyType type = getTypeByName(anchor, PyNames.COLLECTIONS + "." + PyNames.ITERABLE); - if (type instanceof PyClassType) { - final PyClass pyClass = ((PyClassType)type).getPyClass(); - final PyCollectionTypeImpl iterableType = new PyCollectionTypeImpl(pyClass, false, Collections.singletonList(paramType)); - return paramResult.withType(iterableType); + final PyClassType tupleType = PyBuiltinCache.getInstance(anchor).getTupleType();; + // TODO How to represent unbound homogeneous tuple? + if (tupleType != null) { + return paramResult.withType(tupleType); } return EMPTY_RESULT; } else if (starCount == 2) { - final PyType type = getTypeByName(anchor, PyNames.COLLECTIONS + "." + PyNames.MAPPING); - if (type instanceof PyClassType) { - final PyClass pyClass = ((PyClassType)type).getPyClass(); + final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(anchor); + final PyClassType type = builtinCache.getDictType(); + if (type != null) { + final PyClass pyClass = type.getPyClass(); return paramResult.withType(new PyCollectionTypeImpl(pyClass, false, - Arrays.asList(getTypeByName(anchor, "str"), paramType))); + Arrays.asList(builtinCache.getStrType(), paramType))); } return EMPTY_RESULT; } diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/varargsAnnotatedWithFunctionComment.py b/python/testData/inspections/PyUnresolvedReferencesInspection/varargsAnnotatedWithFunctionComment.py new file mode 100644 index 000000000000..bfe4b08a2322 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/varargsAnnotatedWithFunctionComment.py @@ -0,0 +1,4 @@ +def f(*args, **kwargs): + # type: (*str, **str) -> None + args.index('foo') + kwargs.pop('bar') diff --git a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java index a4c0ceefe93d..30d153e00378 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java @@ -291,15 +291,13 @@ public class PyTypeParserTest extends PyTestCase { assertNotNull(type2); assertEquals("MyObject", type2.getName()); - final PyCollectionType type3 = as(parameterTypes.get(2).getType(context), PyCollectionType.class); + final PyClassType type3 = as(parameterTypes.get(2).getType(context), PyClassType.class); assertNotNull(type3); - assertEquals("Iterable", type3.getName()); - assertSize(1, type3.getElementTypes(context)); - assertEquals("str", type3.getElementTypes(context).get(0).getName()); + assertEquals("tuple", type3.getName()); final PyCollectionType type4 = as(parameterTypes.get(3).getType(context), PyCollectionType.class); assertNotNull(type4); - assertEquals("Mapping", type4.getName()); + assertEquals("dict", type4.getName()); assertSize(2, type4.getElementTypes(context)); assertEquals("str", type4.getElementTypes(context).get(0).getName()); assertEquals("int", type4.getElementTypes(context).get(1).getName()); diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index a2fd12393e44..c49b8eb89b80 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -451,7 +451,7 @@ public class PyTypingTest extends PyTestCase { // PY-18254 public void testFunctionTypeComment() { - doTest("(x: int, args: Iterable[float], kwargs: Mapping[str, str]) -> List[bool]", + doTest("(x: int, args: tuple, kwargs: Dict[str, str]) -> List[bool]", "from typing import List\n" + "\n" + "def f(x, *args, **kwargs):\n" + diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 83cc94f1bcf4..72ed58e8d3f6 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -537,6 +537,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase { doTest(); } + // PY-18254 + public void testVarargsAnnotatedWithFunctionComment() { + doTest(); + } + @NotNull @Override protected Class getInspectionClass() {