From ad8b160b9a5048b54fb560642e8ee7ce97657281 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 23 Jul 2013 21:06:21 +0400 Subject: [PATCH] Added function types syntax --- .../python/psi/impl/PyJavaClassType.java | 6 ++ .../python/psi/impl/PyJavaMethodType.java | 6 ++ .../python/psi/types/PyCallableType.java | 10 +++ .../python/psi/types/PyCallableTypeImpl.java | 82 +++++++++++++++++++ .../python/psi/types/PyClassTypeImpl.java | 6 ++ .../python/psi/types/PyFunctionType.java | 6 ++ .../jetbrains/python/psi/types/PyType.flex | 2 +- .../python/psi/types/PyTypeChecker.java | 21 +++++ .../python/psi/types/PyTypeLexer.java | 64 +++++++-------- .../python/psi/types/PyTypeParser.java | 52 ++++++++++-- .../jetbrains/python/PyTypeParserTest.java | 29 +++++++ .../com/jetbrains/python/PyTypeTest.java | 26 ++++++ 12 files changed, 268 insertions(+), 42 deletions(-) create mode 100644 python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java index 56e5ac47c285..7c3a3ac34cc1 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java @@ -96,6 +96,12 @@ public class PyJavaClassType implements PyClassLikeType { return null; } + @Nullable + @Override + public List getParameterTypes(@NotNull TypeEvalContext context) { + return null; + } + @Override public boolean isDefinition() { return myDefinition; diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java index cd186fb95758..2594a1eed1f4 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java @@ -39,6 +39,12 @@ public class PyJavaMethodType implements PyCallableType { return PyJavaTypeProvider.asPyType(myMethod.getReturnType()); } + @Nullable + @Override + public List getParameterTypes(@NotNull TypeEvalContext context) { + return null; + } + @Nullable @Override public List resolveMember(@NotNull String name, diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java index 74d0c2e8637c..83c544fe9762 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java @@ -4,6 +4,8 @@ import com.jetbrains.python.psi.PyQualifiedExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + /** * A type instances of which can possibly be called. For example, a class definition can be called, and the result of a call is a class * instance. @@ -25,4 +27,12 @@ public interface PyCallableType extends PyType { */ @Nullable PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite); + + /** + * Returns the list of parameter types. + * + * @return list of positional parameters or null if not applicable. + */ + @Nullable + List getParameterTypes(@NotNull TypeEvalContext context); } diff --git a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java new file mode 100644 index 000000000000..e1c51ed92ef8 --- /dev/null +++ b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java @@ -0,0 +1,82 @@ +package com.jetbrains.python.psi.types; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.Function; +import com.intellij.util.ProcessingContext; +import com.jetbrains.python.psi.AccessDirection; +import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.PyQualifiedExpression; +import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.resolve.RatedResolveResult; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author vlan + */ +public class PyCallableTypeImpl implements PyCallableType { + @NotNull private final List myParameterTypes; + @Nullable private final PyType myReturnType; + + public PyCallableTypeImpl(@NotNull List parameterTypes, @Nullable PyType returnType) { + myParameterTypes = parameterTypes; + myReturnType = returnType; + } + + @Override + public boolean isCallable() { + return true; + } + + @Nullable + @Override + public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { + return myReturnType; + } + + @Nullable + @Override + public List getParameterTypes(@NotNull TypeEvalContext context) { + return myParameterTypes; + } + + @Nullable + @Override + public List resolveMember(@NotNull String name, + @Nullable PyExpression location, + @NotNull AccessDirection direction, + @NotNull PyResolveContext resolveContext) { + return null; + } + + @Override + public Object[] getCompletionVariants(String completionPrefix, PyExpression location, ProcessingContext context) { + return new Object[0]; + } + + @Nullable + @Override + public String getName() { + return String.format("(%s) -> %s", + StringUtil.join(myParameterTypes, + new Function() { + @Override + public String fun(PyType type) { + return type != null ? type.getName() : "unknown"; + } + }, + ", "), + myReturnType != null ? myReturnType.getName() : "unknown"); + } + + @Override + public boolean isBuiltin(TypeEvalContext context) { + return false; + } + + @Override + public void assertValid(String message) { + } +} diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 555acaeb49f9..2ca0fbcf2c02 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -277,6 +277,12 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { return null; } + @Nullable + @Override + public List getParameterTypes(@NotNull TypeEvalContext context) { + return null; + } + @Nullable private static PsiElement resolveClassMember(@NotNull PyClass cls, boolean isDefinition, diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java index 70123ad3c96a..f0d3a7b76372 100644 --- a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java @@ -33,6 +33,12 @@ public class PyFunctionType implements PyCallableType { return myCallable.getReturnType(context, callSite); } + @Nullable + @Override + public List getParameterTypes(@NotNull TypeEvalContext context) { + return null; + } + @Override public List resolveMember(@NotNull String name, @Nullable PyExpression location, diff --git a/python/src/com/jetbrains/python/psi/types/PyType.flex b/python/src/com/jetbrains/python/psi/types/PyType.flex index 40589a158ba1..5f302214c5e8 100644 --- a/python/src/com/jetbrains/python/psi/types/PyType.flex +++ b/python/src/com/jetbrains/python/psi/types/PyType.flex @@ -26,6 +26,6 @@ import static com.jetbrains.python.psi.types.PyTypeTokenTypes.*; [\r\n]+ { return NL; } [\ \t] { return SPACE; } (":py"?":class:`"[~!]?)|("`")|([A-Z]"{")|("}") { return MARKUP; } -("or")|("of")|("from")|("to")|("<=")|[,\(\)\.\[\]|] { return OP; } +("or")|("of")|("from")|("to")|("<=")|("->")|[,\(\)\.\[\]|] { return OP; } [T-Z] { return PARAMETER; } [A-Za-z_][A-Za-z_0-9]* { return IDENTIFIER; } diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index 6cc3e53ebf0e..429524e49c24 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -140,6 +140,27 @@ public class PyTypeChecker { return true; } } + if (actual instanceof PyCallableType && expected instanceof PyCallableType) { + final PyCallableType expectedCallable = (PyCallableType)expected; + final PyCallableType actualCallable = (PyCallableType)actual; + if (expectedCallable.isCallable() && actualCallable.isCallable()) { + final List expectedParameters = expectedCallable.getParameterTypes(context); + final List actualParameters = actualCallable.getParameterTypes(context); + if (expectedParameters != null && actualParameters != null) { + final int size = Math.min(expectedParameters.size(), actualParameters.size()); + for (int i = 0; i < size; i++) { + if (!match(expectedParameters.get(i), actualParameters.get(i), context, substitutions, recursive)) { + return false; + } + } + } + if (!match(expectedCallable.getCallType(context, null), actualCallable.getCallType(context, null), context, substitutions, + recursive)) { + return false; + } + return true; + } + } final String superName = expected.getName(); final String subName = actual.getName(); // TODO: No inheritance check for builtin numerics at this moment diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java b/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java index d1fcd03fee0e..eea07acda234 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeLexer.java @@ -1,4 +1,4 @@ -/* The following code was generated by JFlex 1.4.3 on 7/22/13 3:52 PM */ +/* The following code was generated by JFlex 1.4.3 on 7/23/13 5:34 PM */ package com.jetbrains.python.psi.types; @@ -11,7 +11,7 @@ import static com.jetbrains.python.psi.types.PyTypeTokenTypes.*; /** * This class is a scanner generated by * JFlex 1.4.3 - * on 7/22/13 3:52 PM from the specification file + * on 7/23/13 5:34 PM from the specification file * /home/user/src/idea/tools/lexer/../../python/src/com/jetbrains/python/psi/types/PyType.flex */ public class PyTypeLexer implements FlexLexer { @@ -35,12 +35,12 @@ public class PyTypeLexer implements FlexLexer { * Translates characters to character classes */ private static final String ZZ_CMAP_PACKED = - "\11\0\1\2\1\1\2\0\1\1\22\0\1\2\1\13\6\0\2\26"+ - "\2\0\1\26\1\0\1\26\1\0\12\31\1\3\1\0\1\24\1\25"+ - "\3\0\23\14\7\27\1\26\1\0\1\26\1\0\1\30\1\12\1\10"+ - "\1\30\1\6\2\30\1\21\5\30\1\7\1\22\1\30\1\17\1\4"+ - "\1\30\1\20\1\11\1\23\4\30\1\5\1\30\1\15\1\26\1\16"+ - "\1\13\uff81\0"; + "\11\0\1\2\1\1\2\0\1\1\22\0\1\2\1\13\6\0\2\30"+ + "\2\0\1\30\1\26\1\30\1\0\12\33\1\3\1\0\1\24\1\25"+ + "\1\27\2\0\23\14\7\31\1\30\1\0\1\30\1\0\1\32\1\12"+ + "\1\10\1\32\1\6\2\32\1\21\5\32\1\7\1\22\1\32\1\17"+ + "\1\4\1\32\1\20\1\11\1\23\4\32\1\5\1\32\1\15\1\30"+ + "\1\16\1\13\uff81\0"; /** * Translates characters to character classes @@ -53,12 +53,12 @@ public class PyTypeLexer implements FlexLexer { private static final int [] ZZ_ACTION = zzUnpackAction(); private static final String ZZ_ACTION_PACKED_0 = - "\1\0\1\1\1\2\1\0\1\3\1\4\4\3\1\0"+ + "\1\0\1\1\1\2\1\0\1\3\1\4\4\3\2\0"+ "\1\5\1\6\2\0\1\5\1\3\2\0\1\3\5\0"+ "\1\4"; private static int [] zzUnpackAction() { - int [] result = new int[26]; + int [] result = new int[27]; int offset = 0; offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); return result; @@ -83,13 +83,13 @@ public class PyTypeLexer implements FlexLexer { private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); private static final String ZZ_ROWMAP_PACKED_0 = - "\0\0\0\32\0\64\0\116\0\150\0\64\0\202\0\234"+ - "\0\266\0\320\0\352\0\64\0\202\0\u0104\0\u011e\0\150"+ - "\0\u0138\0\u0152\0\u016c\0\u0186\0\u01a0\0\u01ba\0\u01d4\0\u01ee"+ - "\0\u0208\0\u0222"; + "\0\0\0\34\0\70\0\124\0\160\0\70\0\214\0\250"+ + "\0\304\0\340\0\374\0\u0118\0\70\0\214\0\u0134\0\u0150"+ + "\0\160\0\u016c\0\u0188\0\u01a4\0\u01c0\0\u01dc\0\u01f8\0\u0214"+ + "\0\u0230\0\u024c\0\u0268"; private static int [] zzUnpackRowMap() { - int [] result = new int[26]; + int [] result = new int[27]; int offset = 0; offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); return result; @@ -114,22 +114,22 @@ public class PyTypeLexer implements FlexLexer { private static final String ZZ_TRANS_PACKED_0 = "\1\0\1\2\1\3\1\4\6\5\1\6\1\0\1\7"+ "\1\0\1\6\1\10\1\5\1\11\1\5\1\12\1\13"+ - "\1\0\1\14\1\15\1\5\2\0\1\2\66\0\1\16"+ - "\1\0\1\17\27\0\6\5\2\0\1\5\2\0\5\5"+ - "\3\0\3\5\4\0\6\5\2\0\1\5\1\6\1\0"+ - "\5\5\3\0\3\5\4\0\6\5\2\0\1\5\2\0"+ - "\1\5\2\20\2\5\3\0\3\5\4\0\6\5\2\0"+ - "\1\5\2\0\1\5\1\21\3\5\3\0\3\5\4\0"+ - "\6\5\2\0\1\5\2\0\1\20\4\5\3\0\3\5"+ - "\25\0\1\14\11\0\1\22\33\0\1\23\26\0\6\5"+ - "\2\0\1\5\2\0\1\24\4\5\3\0\3\5\3\0"+ - "\1\25\36\0\1\26\25\0\6\5\2\0\1\5\2\0"+ - "\3\5\1\20\1\5\3\0\3\5\6\0\1\17\34\0"+ - "\1\27\31\0\1\30\23\0\1\31\40\0\1\32\32\0"+ - "\1\6\16\0"; + "\1\0\1\14\1\0\1\15\1\16\1\5\2\0\1\2"+ + "\72\0\1\17\1\0\1\20\31\0\6\5\2\0\1\5"+ + "\2\0\5\5\5\0\3\5\4\0\6\5\2\0\1\5"+ + "\1\6\1\0\5\5\5\0\3\5\4\0\6\5\2\0"+ + "\1\5\2\0\1\5\2\21\2\5\5\0\3\5\4\0"+ + "\6\5\2\0\1\5\2\0\1\5\1\22\3\5\5\0"+ + "\3\5\4\0\6\5\2\0\1\5\2\0\1\21\4\5"+ + "\5\0\3\5\25\0\1\15\35\0\1\15\11\0\1\23"+ + "\35\0\1\24\30\0\6\5\2\0\1\5\2\0\1\25"+ + "\4\5\5\0\3\5\3\0\1\26\40\0\1\27\27\0"+ + "\6\5\2\0\1\5\2\0\3\5\1\21\1\5\5\0"+ + "\3\5\6\0\1\20\36\0\1\30\33\0\1\31\25\0"+ + "\1\32\42\0\1\33\34\0\1\6\20\0"; private static int [] zzUnpackTrans() { - int [] result = new int[572]; + int [] result = new int[644]; int offset = 0; offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); return result; @@ -170,11 +170,11 @@ public class PyTypeLexer implements FlexLexer { private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); private static final String ZZ_ATTRIBUTE_PACKED_0 = - "\1\0\1\1\1\11\1\0\1\1\1\11\4\1\1\0"+ + "\1\0\1\1\1\11\1\0\1\1\1\11\4\1\2\0"+ "\1\11\1\1\2\0\2\1\2\0\1\1\5\0\1\1"; private static int [] zzUnpackAttribute() { - int [] result = new int[26]; + int [] result = new int[27]; int offset = 0; offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); return result; @@ -257,7 +257,7 @@ public class PyTypeLexer implements FlexLexer { char [] map = new char[0x10000]; int i = 0; /* index in packed string */ int j = 0; /* index in unpacked array */ - while (i < 104) { + while (i < 106) { int count = packed.charAt(i++); char value = packed.charAt(i++); do map[j++] = value; while (--count > 0); diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java index f6a435f70737..8af0776f1825 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java @@ -108,10 +108,11 @@ public class PyTypeParser { final ForwardDeclaration typeExpr = ForwardDeclaration.create(); - final FunctionalParser simpleType = + final FunctionalParser classType = token(IDENTIFIER).then(many(op(".").skipThen(token(IDENTIFIER)))) .map(new MakeSimpleType(anchor)) - .named("simple-type"); + .cached() + .named("class-type"); final FunctionalParser tupleType = op("(").skipThen(typeExpr).then(many(op(",").skipThen(typeExpr))).thenSkip(op(")")) @@ -154,14 +155,13 @@ public class PyTypeParser { .named("type-parameter"); final FunctionalParser simpleExpr = - simpleType + classType .or(tupleType) .or(typeParameter) - .cached() .named("simple-expr"); final FunctionalParser paramExpr = - simpleExpr.thenSkip(op("[")).then(typeExpr).then(many(op(",").skipThen(typeExpr))).thenSkip(op("]")) + classType.thenSkip(op("[")).then(typeExpr).then(many(op(",").skipThen(typeExpr))).thenSkip(op("]")) .map(new Function, List>, ParseResult>() { @Override public ParseResult fun(Pair, List> value) { @@ -187,7 +187,7 @@ public class PyTypeParser { return EMPTY_RESULT; } }) - .or(simpleExpr.thenSkip(op("of")).then(simpleExpr) + .or(classType.thenSkip(op("of")).then(simpleExpr) .map(new Function, ParseResult>() { @Override public ParseResult fun(Pair value) { @@ -205,7 +205,7 @@ public class PyTypeParser { return EMPTY_RESULT; } })) - .or(simpleExpr.thenSkip(op("from")).then(simpleExpr).thenSkip(op("to")).then(simpleExpr) + .or(classType.thenSkip(op("from")).then(simpleExpr).thenSkip(op("to")).then(simpleExpr) .map(new Function, ParseResult>, ParseResult>() { @Override public ParseResult fun(Pair, ParseResult> value) { @@ -222,11 +222,45 @@ public class PyTypeParser { return EMPTY_RESULT; } })) - .or(simpleExpr) .named("param-expr"); + final FunctionalParser callableExpr = + op("(").skipThen(maybe(typeExpr.then(many(op(",").skipThen(typeExpr))))).thenSkip(op(")")).thenSkip(op("->")).then(typeExpr) + .map( + new Function>, ParseResult>, ParseResult>() { + @Override + public ParseResult fun(Pair>, ParseResult> value) { + final List parameterTypes = new ArrayList(); + final ParseResult returnResult = value.getSecond(); + ParseResult result; + final Pair> firstPair = value.getFirst(); + if (firstPair != null) { + final ParseResult first = firstPair.getFirst(); + final List second = firstPair.getSecond(); + result = first; + parameterTypes.add(first.getType()); + for (ParseResult r : second) { + result = result.merge(r); + parameterTypes.add(r.getType()); + } + result = result.merge(returnResult); + } + else { + result = returnResult; + } + return result.withType(new PyCallableTypeImpl(parameterTypes, returnResult.getType())); + } + }) + .named("callable-expr"); + + final FunctionalParser singleExpr = + paramExpr + .or(callableExpr) + .or(simpleExpr) + .named("single-expr"); + final FunctionalParser unionExpr = - paramExpr.then(many(op("or").or(op("|")).skipThen(paramExpr))) + singleExpr.then(many(op("or").or(op("|")).skipThen(singleExpr))) .map(new Function>, ParseResult>() { @Override public ParseResult fun(Pair> value) { diff --git a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java index c7997cfa84c5..ed6850b99fea 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java @@ -227,4 +227,33 @@ public class PyTypeParserTest extends PyTestCase { assertClassType(list.get(1), "str"); assertClassType(list.get(2), "unicode"); } + + public void testCallableType() { + myFixture.configureByFile("typeParser/typeParser.py"); + final PyType type = PyTypeParser.getTypeByName(myFixture.getFile(), "(int, T) -> T"); + assertInstanceOf(type, PyCallableType.class); + final PyCallableType callableType = (PyCallableType)type; + assertNotNull(callableType); + final PyType returnType = callableType.getCallType(getTypeEvalContext(), null); + assertInstanceOf(returnType, PyGenericType.class); + final List parameterTypes = callableType.getParameterTypes(getTypeEvalContext()); + assertNotNull(parameterTypes); + assertEquals(2, parameterTypes.size()); + assertEquals("int", parameterTypes.get(0).getName()); + assertEquals("T", parameterTypes.get(1).getName()); + } + + public void testCallableWithoutArgs() { + myFixture.configureByFile("typeParser/typeParser.py"); + final PyType type = PyTypeParser.getTypeByName(myFixture.getFile(), "() -> int"); + assertInstanceOf(type, PyCallableType.class); + final PyCallableType callableType = (PyCallableType)type; + assertNotNull(callableType); + final PyType returnType = callableType.getCallType(getTypeEvalContext(), null); + assertNotNull(returnType); + assertEquals("int", returnType.getName()); + final List parameterTypes = callableType.getParameterTypes(getTypeEvalContext()); + assertNotNull(parameterTypes); + assertEquals(0, parameterTypes.size()); + } } diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index bd954837daaf..a959be31422a 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -582,6 +582,32 @@ public class PyTypeTest extends PyTestCase { " pass\n"); } + public void testFunctionTypeAsUnificationArgument() { + doTest("int", + "def map2(f, xs):\n" + + " '''\n" + + " :type f: (T) -> V | None\n" + + " :type xs: collections.Iterable[T] | bytes | unicode\n" + + " :rtype: list[V] | bytes | unicode\n" + + " '''\n" + + " pass\n" + + "\n" + + "expr = map2(lambda x: 10, ['1', '2', '3'])[0]\n"); + } + + public void testFunctionTypeAsUnificationResult() { + doTest("int", + "def f(x):\n" + + " '''\n" + + " :type x: T\n" + + " :rtype: () -> T\n" + + " '''\n" + + " pass\n" + + "\n" + + "g = f(10)\n" + + "expr = g()\n"); + } + private static TypeEvalContext getTypeEvalContext(@NotNull PyExpression element) { return TypeEvalContext.userInitiated(element.getContainingFile()).withTracing(); }