From a30ecb2baf63f6b717a2d8cf69d1645f499fa2a3 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 10 Mar 2016 17:08:17 +0300 Subject: [PATCH] PY-18726 Parse list of parameter type in square brackets inside typing.Callable --- .../codeInsight/PyTypingTypeProvider.java | 9 +++++- .../python/psi/types/PyTypeParser.java | 32 ++++++++++++++----- .../com/jetbrains/python/PyTypingTest.java | 10 ++++++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java index d414e9abead1..997cf8dad713 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java @@ -355,7 +355,14 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { return PyUnionType.union(elementTypes.get(0), PyNoneType.INSTANCE); } if ("typing.Callable".equals(qualifiedName) && elementTypes.size() == 2) { - return new PyCallableTypeImpl(null, elementTypes.get(1)); + final List paramTypes = new ArrayList<>(); + final PyTupleType firstType = as(elementTypes.get(0), PyTupleType.class); + if (firstType != null) { + for (int i = 0; i < firstType.getElementCount(); i++) { + paramTypes.add(new PyCallableParameterImpl(null, firstType.getElementType(i))); + } + return new PyCallableTypeImpl(paramTypes, elementTypes.get(1)); + } } if ("typing.Tuple".equals(qualifiedName)) { return PyTupleType.create(resolved, elementTypes.toArray(new PyType[elementTypes.size()])); diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java index 6d0584868f93..69a0bf247bae 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java @@ -23,6 +23,7 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.containers.hash.HashMap; import com.jetbrains.python.PyNames; @@ -325,14 +326,33 @@ public class PyTypeParser { .cached() .named("class-type"); + final FunctionalParser typeList = + op("[").skipThen(maybe(typeExpr.then(many(op(",").skipThen(typeExpr))))).thenSkip(op("]")) + .map(pair -> { + if (pair != null) { + final List itemTypes = new ArrayList<>(); + ParseResult result = pair.getFirst(); + itemTypes.add(pair.getFirst().getType()); + for (ParseResult r : pair.getSecond()) { + result = result.merge(r); + itemTypes.add(r.getType()); + } + final PyTupleType tupleType = PyTupleType.create(anchor, ArrayUtil.toObjectArray(itemTypes, PyType.class)); + return result.withType(tupleType); + } + return EMPTY_RESULT.withType(PyTupleType.create(anchor, new PyType[0])); + }); + + final FunctionalParser typeParam = typeExpr.or(typeList); + final FunctionalParser paramExpr = - classType.thenSkip(op("[")).then(typeExpr).then(many(op(",").skipThen(typeExpr))).thenSkip(op("]")) + classType.thenSkip(op("[")).then(typeParam).then(many(op(",").skipThen(typeParam))).thenSkip(op("]")) .map(value -> { final Pair firstPair = value.getFirst(); final ParseResult first = firstPair.getFirst(); final ParseResult second = firstPair.getSecond(); final List third = value.getSecond(); - final List typesInBrackets = new ArrayList(); + final List typesInBrackets = new ArrayList<>(); typesInBrackets.add(second.getType()); ParseResult result = first; result = result.merge(second); @@ -352,13 +372,9 @@ public class PyTypeParser { }) .named("param-expr"); - final FunctionalParser singleExpr = + typeExpr.define( paramExpr - .or(classType) - .named("single-expr"); - - typeExpr - .define(singleExpr) + .or(classType)) .named("type-expr"); final FunctionalParser argExpr = diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index 889f675ab65c..f797686f0dad 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -472,6 +472,16 @@ public class PyTypingTest extends PyTestCase { } + // PY-18726 + public void testFunctionTypeCommentCallableParameter() { + doTest("(bool, str) -> int", + "from typing import Callable\n" + + "\n" + + "def f(cb):\n" + + " # type: (Callable[[bool, str], int]) -> None\n" + + " expr = cb"); + } + // PY-18386 public void testRecursiveType() { doTest("Union[int, Any]",