From f0222e8fa378143bc2a482a661116ee7bcbfe255 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 2 Aug 2013 11:40:59 +0400 Subject: [PATCH 01/24] Added dependencies to main_pycharm on TextMate bundles and CoffeeScript plugins --- python/main_pycharm.iml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/main_pycharm.iml b/python/main_pycharm.iml index b05917390a2a..981f6a1ce273 100644 --- a/python/main_pycharm.iml +++ b/python/main_pycharm.iml @@ -47,6 +47,8 @@ + + From afd5c68804ca55079965172d5d1d95b42bdbe15e Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 2 Aug 2013 12:41:13 +0400 Subject: [PATCH 02/24] Fixed possible NPE --- .../jetbrains/python/inspections/PyTypeCheckerInspection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java index 9002e762105f..757664c8915f 100644 --- a/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyTypeCheckerInspection.java @@ -63,7 +63,7 @@ public class PyTypeCheckerInspection extends PyInspection { final PyExpression source = node.getForPart().getSource(); if (source != null) { final PyType type = myTypeEvalContext.getType(source); - if (!PyTypeChecker.isUnknown(type) && !PyABCUtil.isSubtype(type, PyNames.ITERABLE)) { + if (type != null && !PyTypeChecker.isUnknown(type) && !PyABCUtil.isSubtype(type, PyNames.ITERABLE)) { registerProblem(source, String.format("Expected 'collections.Iterable', got '%s' instead", PythonDocumentationProvider.getTypeName(type, myTypeEvalContext))); } From bbcee93c26ff557cc29212fa3e0890f02fa97c6a Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 2 Aug 2013 15:41:20 +0400 Subject: [PATCH 03/24] Use object instead of pair for describing function type parameters --- .../python/psi/impl/PyJavaClassType.java | 4 +- .../python/psi/impl/PyJavaMethodType.java | 4 +- .../python/psi/types/PyCallableParameter.java | 19 +++++++ .../python/psi/types/PyCallableType.java | 5 +- .../documentation/PyTypeModelBuilder.java | 7 ++- .../psi/types/PyCallableParameterImpl.java | 49 +++++++++++++++++++ .../python/psi/types/PyCallableTypeImpl.java | 16 +++--- .../python/psi/types/PyClassTypeImpl.java | 2 +- .../python/psi/types/PyFunctionType.java | 11 ++--- .../python/psi/types/PyTypeChecker.java | 31 ++++++------ .../python/psi/types/PyTypeParser.java | 6 +-- .../jetbrains/python/PyTypeParserTest.java | 13 +++-- 12 files changed, 118 insertions(+), 49 deletions(-) create mode 100644 python/psi-api/src/com/jetbrains/python/psi/types/PyCallableParameter.java create mode 100644 python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java index adbe3806074f..5a74a6e0f1a3 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java @@ -1,6 +1,5 @@ package com.jetbrains.python.psi.impl; -import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiField; import com.intellij.psi.PsiMethod; @@ -12,6 +11,7 @@ import com.jetbrains.python.psi.PyQualifiedExpression; import com.jetbrains.python.psi.resolve.CompletionVariantsProcessor; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.RatedResolveResult; +import com.jetbrains.python.psi.types.PyCallableParameter; import com.jetbrains.python.psi.types.PyClassLikeType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; @@ -101,7 +101,7 @@ public class PyJavaClassType implements PyClassLikeType { @Nullable @Override - public List> getParameters(@NotNull TypeEvalContext context) { + public List getParameters(@NotNull TypeEvalContext context) { return null; } diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java index 3ef7355229b9..180ad2377503 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java @@ -1,6 +1,5 @@ package com.jetbrains.python.psi.impl; -import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; import com.intellij.util.ArrayUtil; @@ -10,6 +9,7 @@ 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 com.jetbrains.python.psi.types.PyCallableParameter; import com.jetbrains.python.psi.types.PyCallableType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; @@ -42,7 +42,7 @@ public class PyJavaMethodType implements PyCallableType { @Nullable @Override - public List> getParameters(@NotNull TypeEvalContext context) { + public List getParameters(@NotNull TypeEvalContext context) { return null; } diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableParameter.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableParameter.java new file mode 100644 index 000000000000..9347e22b3d2d --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableParameter.java @@ -0,0 +1,19 @@ +package com.jetbrains.python.psi.types; + +import com.jetbrains.python.psi.PyParameter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author vlan + */ +public interface PyCallableParameter { + @Nullable + String getName(); + + @Nullable + PyType getType(); + + @Nullable + PyParameter getParameter(); +} 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 482df90af817..261a7eb443d2 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 @@ -1,6 +1,5 @@ package com.jetbrains.python.psi.types; -import com.intellij.openapi.util.Pair; import com.jetbrains.python.psi.PyQualifiedExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,8 +31,8 @@ public interface PyCallableType extends PyType { /** * Returns the list of parameter types. * - * @return list of (name, type) pairs or null if not applicable. Name and type in pair may be null. + * @return list of parameter info null if not applicable. */ @Nullable - List> getParameters(@NotNull TypeEvalContext context); + List getParameters(@NotNull TypeEvalContext context); } diff --git a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java index 8e6db24d6d8b..8a37daa6b275 100644 --- a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java @@ -4,7 +4,6 @@ import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.Callable; @@ -206,11 +205,11 @@ public class PyTypeModelBuilder { private TypeModel build(@NotNull PyCallableType type) { List parameterModels = null; - final List> parameters = type.getParameters(myContext); + final List parameters = type.getParameters(myContext); if (parameters != null) { parameterModels = new ArrayList(); - for (Pair parameter : parameters) { - parameterModels.add(new ParamType(parameter.getFirst(), build(parameter.getSecond(), true))); + for (PyCallableParameter parameter : parameters) { + parameterModels.add(new ParamType(parameter.getName(), build(parameter.getType(), true))); } } final PyType ret = type.getCallType(myContext, null); diff --git a/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java b/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java new file mode 100644 index 000000000000..2d99cdbf20d6 --- /dev/null +++ b/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java @@ -0,0 +1,49 @@ +package com.jetbrains.python.psi.types; + +import com.jetbrains.python.psi.PyParameter; +import org.jetbrains.annotations.Nullable; + +/** + * @author vlan + */ +public class PyCallableParameterImpl implements PyCallableParameter { + @Nullable private final String myName; + @Nullable private final PyType myType; + @Nullable private final PyParameter myElement; + + public PyCallableParameterImpl(@Nullable String name, @Nullable PyType type) { + myName = name; + myType = type; + myElement = null; + } + + public PyCallableParameterImpl(@Nullable PyParameter element, @Nullable PyType type) { + myName = null; + myType = type; + myElement = element; + } + + @Nullable + @Override + public String getName() { + if (myName != null) { + return myName; + } + else if (myElement != null) { + return myElement.getName(); + } + return null; + } + + @Nullable + @Override + public PyType getType() { + return myType; + } + + @Nullable + @Override + public PyParameter getParameter() { + return myElement; + } +} diff --git a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java index 8b3cb754b437..9e655f9036e9 100644 --- a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java @@ -1,6 +1,5 @@ package com.jetbrains.python.psi.types; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Function; import com.intellij.util.ProcessingContext; @@ -19,10 +18,10 @@ import java.util.List; * @author vlan */ public class PyCallableTypeImpl implements PyCallableType { - @Nullable private final List> myParameters; + @Nullable private final List myParameters; @Nullable private final PyType myReturnType; - public PyCallableTypeImpl(@Nullable List> parameters, @Nullable PyType returnType) { + public PyCallableTypeImpl(@Nullable List parameters, @Nullable PyType returnType) { myParameters = parameters; myReturnType = returnType; } @@ -40,7 +39,7 @@ public class PyCallableTypeImpl implements PyCallableType { @Nullable @Override - public List> getParameters(@NotNull TypeEvalContext context) { + public List getParameters(@NotNull TypeEvalContext context) { return myParameters; } @@ -61,16 +60,17 @@ public class PyCallableTypeImpl implements PyCallableType { @Nullable @Override public String getName() { + final TypeEvalContext context = TypeEvalContext.codeInsightFallback(); return String.format("(%s) -> %s", myParameters != null ? StringUtil.join(myParameters, - new Function, String>() { + new Function() { @Override - public String fun(Pair param) { + public String fun(PyCallableParameter param) { if (param != null) { final StringBuilder builder = new StringBuilder(); - final String name = param.getFirst(); - final PyType type = param.getSecond(); + final String name = param.getName(); + final PyType type = param.getType(); if (name != null) { builder.append(name); if (type != null) { diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 446aba417165..de9cc6b39c68 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -279,7 +279,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { @Nullable @Override - public List> getParameters(@NotNull TypeEvalContext context) { + public List getParameters(@NotNull TypeEvalContext context) { return null; } diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java index 20072ba86284..fd3e3a8f4d27 100644 --- a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java @@ -1,6 +1,5 @@ package com.jetbrains.python.psi.types; -import com.intellij.openapi.util.Pair; import com.intellij.util.ProcessingContext; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.PyResolveContext; @@ -37,13 +36,11 @@ public class PyFunctionType implements PyCallableType { @Nullable @Override - public List> getParameters(@NotNull TypeEvalContext context) { - final List> result = new ArrayList>(); + public List getParameters(@NotNull TypeEvalContext context) { + final List result = new ArrayList(); for (PyParameter parameter : myCallable.getParameterList().getParameters()) { - if (parameter instanceof PyNamedParameter) { - final PyNamedParameter namedParameter = (PyNamedParameter)parameter; - result.add(Pair.create(getParameterName(namedParameter), context.getType(namedParameter))); - } + final PyType type = parameter instanceof PyTypedElement ? context.getType((PyTypedElement)parameter) : null; + result.add(new PyCallableParameterImpl(parameter, type)); } return result; } diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index 4c3660dcbdb3..92ae7ab35caf 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -1,6 +1,5 @@ package com.jetbrains.python.psi.types; -import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.PsiReference; @@ -145,15 +144,15 @@ public class PyTypeChecker { final PyCallableType expectedCallable = (PyCallableType)expected; final PyCallableType actualCallable = (PyCallableType)actual; if (expectedCallable.isCallable() && actualCallable.isCallable()) { - final List> expectedParameters = expectedCallable.getParameters(context); - final List> actualParameters = actualCallable.getParameters(context); + final List expectedParameters = expectedCallable.getParameters(context); + final List actualParameters = actualCallable.getParameters(context); if (expectedParameters != null && actualParameters != null) { final int size = Math.min(expectedParameters.size(), actualParameters.size()); for (int i = 0; i < size; i++) { - final Pair expectedParam = expectedParameters.get(i); - final Pair actualParam = actualParameters.get(i); + final PyCallableParameter expectedParam = expectedParameters.get(i); + final PyCallableParameter actualParam = actualParameters.get(i); // TODO: Check named and star params, not only positional ones - if (!match(expectedParam.getSecond(), actualParam.getSecond(), context, substitutions, recursive)) { + if (!match(expectedParam.getType(), actualParam.getType(), context, substitutions, recursive)) { return false; } } @@ -234,11 +233,11 @@ public class PyTypeChecker { } else if (type instanceof PyCallableType) { final PyCallableType callable = (PyCallableType)type; - final List> parameters = callable.getParameters(context); + final List parameters = callable.getParameters(context); if (parameters != null) { - for (Pair parameter : parameters) { + for (PyCallableParameter parameter : parameters) { if (parameter != null) { - collectGenerics(parameter.getSecond(), context, collected, visited); + collectGenerics(parameter.getType(), context, collected, visited); } } } @@ -280,12 +279,16 @@ public class PyTypeChecker { } else if (type instanceof PyCallableType) { final PyCallableType callable = (PyCallableType)type; - List> substParams = null; - final List> parameters = callable.getParameters(context); + List substParams = null; + final List parameters = callable.getParameters(context); if (parameters != null) { - substParams = new ArrayList>(); - for (Pair parameter : parameters) { - substParams.add(Pair.create(parameter.getFirst(), substitute(parameter.getSecond(), substitutions, context))); + substParams = new ArrayList(); + for (PyCallableParameter parameter : parameters) { + final PyType substType = substitute(parameter.getType(), substitutions, context); + final PyCallableParameter subst = parameter.getParameter() != null ? + new PyCallableParameterImpl(parameter.getParameter(), substType) : + new PyCallableParameterImpl(parameter.getName(), substType); + substParams.add(subst); } } final PyType substResult = substitute(callable.getCallType(context, null), substitutions, context); diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java index 7aab2400b333..7150ab1413ae 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeParser.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeParser.java @@ -230,7 +230,7 @@ public class PyTypeParser { new Function>, ParseResult>, ParseResult>() { @Override public ParseResult fun(Pair>, ParseResult> value) { - final List> parameters = new ArrayList>(); + final List parameters = new ArrayList(); final ParseResult returnResult = value.getSecond(); ParseResult result; final Pair> firstPair = value.getFirst(); @@ -238,10 +238,10 @@ public class PyTypeParser { final ParseResult first = firstPair.getFirst(); final List second = firstPair.getSecond(); result = first; - parameters.add(Pair.create(null, first.getType())); + parameters.add(new PyCallableParameterImpl((String)null, first.getType())); for (ParseResult r : second) { result = result.merge(r); - parameters.add(Pair.create(null, r.getType())); + parameters.add(new PyCallableParameterImpl((String)null, r.getType())); } result = result.merge(returnResult); } diff --git a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java index 105893830283..9407a30d2363 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java @@ -1,6 +1,5 @@ package com.jetbrains.python; -import com.intellij.openapi.util.Pair; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.types.*; @@ -237,11 +236,15 @@ public class PyTypeParserTest extends PyTestCase { assertNotNull(callableType); final PyType returnType = callableType.getCallType(getTypeEvalContext(), null); assertInstanceOf(returnType, PyGenericType.class); - final List> parameterTypes = callableType.getParameters(getTypeEvalContext()); + final List parameterTypes = callableType.getParameters(getTypeEvalContext()); assertNotNull(parameterTypes); assertEquals(2, parameterTypes.size()); - assertEquals("int", parameterTypes.get(0).getSecond().getName()); - assertEquals("T", parameterTypes.get(1).getSecond().getName()); + final PyType type0 = parameterTypes.get(0).getType(); + assertNotNull(type0); + assertEquals("int", type0.getName()); + final PyType type1 = parameterTypes.get(1).getType(); + assertNotNull(type1); + assertEquals("T", type1.getName()); } public void testCallableWithoutArgs() { @@ -253,7 +256,7 @@ public class PyTypeParserTest extends PyTestCase { final PyType returnType = callableType.getCallType(getTypeEvalContext(), null); assertNotNull(returnType); assertEquals("int", returnType.getName()); - final List> parameterTypes = callableType.getParameters(getTypeEvalContext()); + final List parameterTypes = callableType.getParameters(getTypeEvalContext()); assertNotNull(parameterTypes); assertEquals(0, parameterTypes.size()); } From 95cd5d03b06f3034bfdf277df0918a2421fc8123 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 2 Aug 2013 15:49:31 +0400 Subject: [PATCH 04/24] Made Callable a typed element --- python/psi-api/src/com/jetbrains/python/psi/Callable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/Callable.java b/python/psi-api/src/com/jetbrains/python/psi/Callable.java index 40505a9e1c04..c156564506a3 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/Callable.java +++ b/python/psi-api/src/com/jetbrains/python/psi/Callable.java @@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable; * @author dcheryasov */ -public interface Callable extends PyElement { +public interface Callable extends PyTypedElement { /** * @return a list of parameters passed to this callable, possibly empty. From 8a0f01116a3ca96fcd93e37fd13f396d8f3fa62c Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 2 Aug 2013 15:54:01 +0400 Subject: [PATCH 05/24] Fixed nullness annotations --- .../python/findUsages/PyUsageTypeProvider.java | 2 +- .../python/psi/impl/CallArgumentsMappingImpl.java | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/python/src/com/jetbrains/python/findUsages/PyUsageTypeProvider.java b/python/src/com/jetbrains/python/findUsages/PyUsageTypeProvider.java index 23a538cb632e..9114b10c3d84 100644 --- a/python/src/com/jetbrains/python/findUsages/PyUsageTypeProvider.java +++ b/python/src/com/jetbrains/python/findUsages/PyUsageTypeProvider.java @@ -74,7 +74,7 @@ public class PyUsageTypeProvider implements UsageTypeProviderEx { } @Nullable - private static UsageType checkMatchingSignatureGroup(PyCallExpression call, UsageTarget[] targets, TypeEvalContext context) { + private static UsageType checkMatchingSignatureGroup(PyCallExpression call, UsageTarget[] targets, @NotNull TypeEvalContext context) { if (targets.length == 1 && targets[0] instanceof PsiElementUsageTarget) { final PsiElement element = ((PsiElementUsageTarget)targets[0]).getElement(); if (element instanceof PyFunction) { diff --git a/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java b/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java index e8cddd605855..1df8ec4b5d62 100644 --- a/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java @@ -49,7 +49,7 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping { * @param resolved_callee what to map parameters of * @param context optional shared type evaluator / cache. */ - public void mapArguments(PyCallExpression.PyMarkedCallee resolved_callee, @Nullable TypeEvalContext context) { + public void mapArguments(PyCallExpression.PyMarkedCallee resolved_callee, @NotNull TypeEvalContext context) { PyExpression[] arguments = myArgumentList.getArguments(); myMarkedCallee = resolved_callee; List unmatched_args = new LinkedList(); @@ -206,11 +206,9 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping { if (cnt < parameters.length && cnt < positional_index && myTupleArg != null) { // check length of myTupleArg PyType tuple_arg_type = null; - if (context != null) { - final PyExpression expression = PsiTreeUtil.getChildOfType(myTupleArg, PyExpression.class); - if (expression != null) { - tuple_arg_type = context.getType(expression); - } + final PyExpression expression = PsiTreeUtil.getChildOfType(myTupleArg, PyExpression.class); + if (expression != null) { + tuple_arg_type = context.getType(expression); } int tuple_length; boolean tuple_length_known; From 8b0b58901f6b2ea1851adc33a047e2c6589b9fa4 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 2 Aug 2013 16:51:42 +0400 Subject: [PATCH 06/24] Use parameters from function type for matching arguments --- .../psi/impl/CallArgumentsMappingImpl.java | 510 +++++++++--------- 1 file changed, 262 insertions(+), 248 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java b/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java index 1df8ec4b5d62..05025e5fb9c6 100644 --- a/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java @@ -3,9 +3,7 @@ package com.jetbrains.python.psi.impl; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.types.PyTupleType; -import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.psi.types.TypeEvalContext; +import com.jetbrains.python.psi.types.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -75,266 +73,282 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping { } } } - // prepare parameter slots - final PyParameter[] parameters = myMarkedCallee.getCallable().getParameterList().getParameters(); - Map slots = new LinkedHashMap(); - PyNamedParameter kwd_par = null; // **param - PyNamedParameter tuple_par = null; // *param - Set mapped_args = new HashSet(); - final int implicit_offset = resolved_callee.getImplicitOffset(); - int positional_index = 0; // up to this index parameters are positional - // check positional arguments, fill slots - int i = 0; - for (PyParameter par : parameters) { - if (tuple_par == null && kwd_par == null && positional_index < implicit_offset) { - positional_index += 1; - continue; - } - PyNamedParameter n_par = par.getAsNamed(); - if (n_par != null) { - if (n_par.isPositionalContainer()) tuple_par = n_par; - else if (n_par.isKeywordContainer()) kwd_par = n_par; - else { - slots.put(n_par, null); // regular parameter that may serve as positional/named - if (tuple_par == null && kwd_par == null) { - positional_index += 1; // only if we're not past *param / **param + PyType type = context.getType(myMarkedCallee.getCallable()); + if (type instanceof PyUnionType) { + type = ((PyUnionType)type).excludeNull(); + } + if (type instanceof PyCallableType) { + final PyCallableType callableType = (PyCallableType)type; + final List callableTypeParameters = callableType.getParameters(context); + if (callableTypeParameters != null) { + final List parameters = new ArrayList(); + for (PyCallableParameter callableParameter : callableTypeParameters) { + final PyParameter parameter = callableParameter.getParameter(); + if (parameter == null) { + return; } + parameters.add(parameter); } - } - else { - PyTupleParameter t_par = par.getAsTuple(); - if (t_par != null) positional_index += 1; // tuple can only be positional - // else lone star, skip - } - i += 1; - } - // rule out 'self' or other implicit params - for (i=0; i < implicit_offset && i < parameters.length; i+=1) { - slots.remove(parameters[i].getAsNamed()); - positional_index += 1; - } - // now params to the left of positional_index are positional. - // map positional args to positional params. - // we assume that implicitly skipped parameters are never nested tuples. no idea when they could ever be. - int cnt = implicit_offset; - int positional_bound = arguments.length; // to the right of this pos args are verboten - ListIterator unmatched_arg_iter = unmatched_args.listIterator(); - while (unmatched_arg_iter.hasNext()) { - PyExpression arg = unmatched_arg_iter.next(); - if (arg instanceof PyStarArgument || arg instanceof PyKeywordArgument) { - positional_bound = cnt; - break; - } - if (cnt < parameters.length && cnt < positional_index) { - final PyParameter par = parameters[cnt]; - PyNamedParameter n_par = par.getAsNamed(); - if (n_par != null) { - cnt += 1; - slots.put(n_par, PyUtil.peelArgument(arg)); - mapped_args.add(arg); - } - else { - PyTupleParameter t_par = par.getAsTuple(); - if (t_par != null) { - if (arg instanceof PyParenthesizedExpression) { - mapped_args.add(arg); // tuple itself is always mapped; its insides can fail - } + // prepare parameter slots + Map slots = new LinkedHashMap(); + PyNamedParameter kwd_par = null; // **param + PyNamedParameter tuple_par = null; // *param + Set mapped_args = new HashSet(); + final int implicit_offset = resolved_callee.getImplicitOffset(); + int positional_index = 0; // up to this index parameters are positional + // check positional arguments, fill slots + int i = 0; + for (PyParameter par : parameters) { + if (tuple_par == null && kwd_par == null && positional_index < implicit_offset) { + positional_index += 1; + continue; + } + PyNamedParameter n_par = par.getAsNamed(); + if (n_par != null) { + if (n_par.isPositionalContainer()) tuple_par = n_par; + else if (n_par.isKeywordContainer()) kwd_par = n_par; else { - PyType arg_type = context.getType(arg); - if (arg_type != null && arg_type.isBuiltin(context) && "list".equals(arg_type.getName())) { - mapped_args.add(arg); // we can't really analyze arbitrary lists statically yet - // but ListLiteralExpressions are handled by visitor + slots.put(n_par, null); // regular parameter that may serve as positional/named + if (tuple_par == null && kwd_par == null) { + positional_index += 1; // only if we're not past *param / **param } } - unmatched_arg_iter.previous(); - MyParamVisitor visitor = new MyParamVisitor(unmatched_arg_iter, this); - visitor.enterTuple(t_par.getAsTuple()); // will recur as needed - unmatched_subargs.addAll(visitor.getUnmatchedSubargs()); // what didn't match inside - cnt += 1; } - // else: goes to *param - } - } - else break; - } - // anything left after mapping of nested-tuple params? - for (Map.Entry> pair : myNestedMappedParams.entrySet()) { - PyExpression arg = pair.getKey(); - List params = pair.getValue(); - mapped_args.add(arg); - for (PyNamedParameter n_par : params) slots.remove(n_par); - } - for (PyExpression arg : unmatched_subargs) { - markArgument(arg, ArgFlag.IS_UNMAPPED); - } - // mark past-bound positional args - i = positional_bound; - while (i parameter_by_name = new LinkedHashMap(); - for (PyParameter par : parameters) { - PyNamedParameter n_par = par.getAsNamed(); - if (n_par != null) parameter_by_name.put(n_par.getName(), n_par); - } - for (PyExpression arg : arguments) { - if (arg instanceof PyKeywordArgument) { // to explicitly named param? - String arg_name = ((PyKeywordArgument)arg).getKeyword(); - PyNamedParameter respective_par = parameter_by_name.get(arg_name); - if (respective_par != null && !respective_par.isKeywordContainer() && !respective_par.isPositionalContainer()) { - if (slots.get(respective_par) != null) markArgument(arg, ArgFlag.IS_DUP); - else slots.put(respective_par, arg); - } - else { // to **param? - if (kwd_par != null) { - myPlainMappedParams.put(arg, kwd_par); - mapped_args.add(arg); + else { + PyTupleParameter t_par = par.getAsTuple(); + if (t_par != null) positional_index += 1; // tuple can only be positional + // else lone star, skip } - } - seen_named_args = true; - } - } - // map *arg to positional params if possible - boolean tuple_arg_not_exhausted = false; - boolean tuple_dup_found = false; - if (cnt < parameters.length && cnt < positional_index && myTupleArg != null) { - // check length of myTupleArg - PyType tuple_arg_type = null; - final PyExpression expression = PsiTreeUtil.getChildOfType(myTupleArg, PyExpression.class); - if (expression != null) { - tuple_arg_type = context.getType(expression); - } - int tuple_length; - boolean tuple_length_known; - if (tuple_arg_type instanceof PyTupleType) { - tuple_length = ((PyTupleType)tuple_arg_type).getElementCount(); - tuple_length_known = true; - } - else { - tuple_length = 2000000; // no practical function will have so many positional params - tuple_length_known = false; - } - int mapped_params_count = 0; - while (cnt < parameters.length && cnt < positional_index && mapped_params_count < tuple_length) { - PyParameter par = parameters[cnt]; - if (par instanceof PySingleStarParameter) break; - PyNamedParameter n_par = par.getAsNamed(); - if (slots.containsKey(n_par)) { - final PyExpression arg_here = slots.get(n_par); - if (arg_here != null) { - if (tuple_length_known) { - final EnumSet flags = myArgFlags.get(arg_here); - if (flags == null || flags.isEmpty()) { - markArgument(arg_here, ArgFlag.IS_DUP); - tuple_dup_found = true; - } - } - // else: unknown tuple length is just enough - // the spree is over - break; - } - else if (n_par != null) { // normally always true - myTupleMappedParams.add(n_par); - mapped_args.add(myTupleArg); - slots.remove(n_par); - } - } - else if (n_par == tuple_par) { - mapped_params_count = tuple_length; // we found *param for our *arg, consider it fully mapped - break; - } - cnt += 1; - mapped_params_count += 1; - } - if ( - tuple_length_known && (mapped_params_count < tuple_length) || // not exhausted - mapped_params_count == 0 // unknown length must consume at least first param - ) { - tuple_arg_not_exhausted = true; - } - } - // map *param to the leftmost chunk of unmapped positional args - // NOTE: ignores the structure of nested-tuple params! - if (tuple_par != null) { - i = 0; - while (i < arguments.length && mapped_args.contains(arguments[i]) && isPositionalArg(arguments[i])) { - i += 1; // skip first mapped args - } - if (i < arguments.length && isPositionalArg(arguments[i])) { - while (i < arguments.length && !mapped_args.contains(arguments[i]) && isPositionalArg(arguments[i])) { - myPlainMappedParams.put(arguments[i], tuple_par); - mapped_args.add(arguments[i]); i += 1; } - } - } - // map unmapped *arg to *param - if (myTupleArg != null && tuple_par != null) { - if (!mapped_args.contains(myTupleArg)) { - myTupleMappedParams.add(tuple_par); - mapped_args.add(myTupleArg); - } - else if (! seen_named_args && tuple_arg_not_exhausted) { - // case of (*(1, 2, 3)) -> (a, *b); map the rest of *arg to *param - myTupleMappedParams.add(tuple_par); - mapped_args.add(myTupleArg); - tuple_arg_not_exhausted = false; - } - } - if (tuple_arg_not_exhausted && ! tuple_dup_found) { - markArgument(myTupleArg, ArgFlag.IS_TOO_LONG); - } - // map unmapped named params to **kwarg - if (myKwdArg != null) { - for (PyParameter par : parameters) { - PyNamedParameter n_par = par.getAsNamed(); - if (n_par != null && !n_par.isKeywordContainer() && !n_par.isPositionalContainer() && slots.get(n_par) == null) { - slots.put(n_par, myKwdArg); + // rule out 'self' or other implicit params + for (i=0; i < implicit_offset && i < parameters.size(); i+=1) { + slots.remove(parameters.get(i).getAsNamed()); + positional_index += 1; } - } - } - // map unmapped **kwarg to **param - if (myKwdArg != null && kwd_par != null && !mapped_args.contains(myKwdArg)) { - myKwdMappedParams.add(kwd_par); - mapped_args.add(myKwdArg); - } - // fill in ret, mark unmapped named params - for (Map.Entry pair : slots.entrySet()) { - PyNamedParameter n_par = pair.getKey(); - PyExpression arg = pair.getValue(); - if (arg == null) { - if (!n_par.hasDefaultValue()) myUnmappedParams.add(n_par); - } - else { - if (arg == myTupleArg) { - myTupleMappedParams.add(n_par); + // now params to the left of positional_index are positional. + // map positional args to positional params. + // we assume that implicitly skipped parameters are never nested tuples. no idea when they could ever be. + int cnt = implicit_offset; + int positional_bound = arguments.length; // to the right of this pos args are verboten + ListIterator unmatched_arg_iter = unmatched_args.listIterator(); + while (unmatched_arg_iter.hasNext()) { + PyExpression arg = unmatched_arg_iter.next(); + if (arg instanceof PyStarArgument || arg instanceof PyKeywordArgument) { + positional_bound = cnt; + break; + } + if (cnt < parameters.size() && cnt < positional_index) { + final PyParameter par = parameters.get(cnt); + PyNamedParameter n_par = par.getAsNamed(); + if (n_par != null) { + cnt += 1; + slots.put(n_par, PyUtil.peelArgument(arg)); + mapped_args.add(arg); + } + else { + PyTupleParameter t_par = par.getAsTuple(); + if (t_par != null) { + if (arg instanceof PyParenthesizedExpression) { + mapped_args.add(arg); // tuple itself is always mapped; its insides can fail + } + else { + PyType arg_type = context.getType(arg); + if (arg_type != null && arg_type.isBuiltin(context) && "list".equals(arg_type.getName())) { + mapped_args.add(arg); // we can't really analyze arbitrary lists statically yet + // but ListLiteralExpressions are handled by visitor + } + } + unmatched_arg_iter.previous(); + MyParamVisitor visitor = new MyParamVisitor(unmatched_arg_iter, this); + visitor.enterTuple(t_par.getAsTuple()); // will recur as needed + unmatched_subargs.addAll(visitor.getUnmatchedSubargs()); // what didn't match inside + cnt += 1; + } + // else: goes to *param + } + } + else break; } - else if (arg == myKwdArg) { - myKwdMappedParams.add(n_par); + // anything left after mapping of nested-tuple params? + for (Map.Entry> pair : myNestedMappedParams.entrySet()) { + PyExpression arg = pair.getKey(); + List params = pair.getValue(); + mapped_args.add(arg); + for (PyNamedParameter n_par : params) slots.remove(n_par); } - else { - myPlainMappedParams.put(arg, n_par); - } - } - } - // mark unmapped args - for (PyExpression arg : slots.values()) { - if (arg != null) mapped_args.add(arg); - } - for (PyExpression arg : arguments) { - if (!mapped_args.contains(arg)) { - final EnumSet flags = myArgFlags.get(arg); - if (flags == null || flags.isEmpty()) { + for (PyExpression arg : unmatched_subargs) { markArgument(arg, ArgFlag.IS_UNMAPPED); } + // mark past-bound positional args + i = positional_bound; + while (i parameter_by_name = new LinkedHashMap(); + for (PyParameter par : parameters) { + PyNamedParameter n_par = par.getAsNamed(); + if (n_par != null) parameter_by_name.put(n_par.getName(), n_par); + } + for (PyExpression arg : arguments) { + if (arg instanceof PyKeywordArgument) { // to explicitly named param? + String arg_name = ((PyKeywordArgument)arg).getKeyword(); + PyNamedParameter respective_par = parameter_by_name.get(arg_name); + if (respective_par != null && !respective_par.isKeywordContainer() && !respective_par.isPositionalContainer()) { + if (slots.get(respective_par) != null) markArgument(arg, ArgFlag.IS_DUP); + else slots.put(respective_par, arg); + } + else { // to **param? + if (kwd_par != null) { + myPlainMappedParams.put(arg, kwd_par); + mapped_args.add(arg); + } + } + seen_named_args = true; + } + } + // map *arg to positional params if possible + boolean tuple_arg_not_exhausted = false; + boolean tuple_dup_found = false; + if (cnt < parameters.size() && cnt < positional_index && myTupleArg != null) { + // check length of myTupleArg + PyType tuple_arg_type = null; + final PyExpression expression = PsiTreeUtil.getChildOfType(myTupleArg, PyExpression.class); + if (expression != null) { + tuple_arg_type = context.getType(expression); + } + int tuple_length; + boolean tuple_length_known; + if (tuple_arg_type instanceof PyTupleType) { + tuple_length = ((PyTupleType)tuple_arg_type).getElementCount(); + tuple_length_known = true; + } + else { + tuple_length = 2000000; // no practical function will have so many positional params + tuple_length_known = false; + } + int mapped_params_count = 0; + while (cnt < parameters.size() && cnt < positional_index && mapped_params_count < tuple_length) { + PyParameter par = parameters.get(cnt); + if (par instanceof PySingleStarParameter) break; + PyNamedParameter n_par = par.getAsNamed(); + if (slots.containsKey(n_par)) { + final PyExpression arg_here = slots.get(n_par); + if (arg_here != null) { + if (tuple_length_known) { + final EnumSet flags = myArgFlags.get(arg_here); + if (flags == null || flags.isEmpty()) { + markArgument(arg_here, ArgFlag.IS_DUP); + tuple_dup_found = true; + } + } + // else: unknown tuple length is just enough + // the spree is over + break; + } + else if (n_par != null) { // normally always true + myTupleMappedParams.add(n_par); + mapped_args.add(myTupleArg); + slots.remove(n_par); + } + } + else if (n_par == tuple_par) { + mapped_params_count = tuple_length; // we found *param for our *arg, consider it fully mapped + break; + } + cnt += 1; + mapped_params_count += 1; + } + if ( + tuple_length_known && (mapped_params_count < tuple_length) || // not exhausted + mapped_params_count == 0 // unknown length must consume at least first param + ) { + tuple_arg_not_exhausted = true; + } + } + // map *param to the leftmost chunk of unmapped positional args + // NOTE: ignores the structure of nested-tuple params! + if (tuple_par != null) { + i = 0; + while (i < arguments.length && mapped_args.contains(arguments[i]) && isPositionalArg(arguments[i])) { + i += 1; // skip first mapped args + } + if (i < arguments.length && isPositionalArg(arguments[i])) { + while (i < arguments.length && !mapped_args.contains(arguments[i]) && isPositionalArg(arguments[i])) { + myPlainMappedParams.put(arguments[i], tuple_par); + mapped_args.add(arguments[i]); + i += 1; + } + } + } + // map unmapped *arg to *param + if (myTupleArg != null && tuple_par != null) { + if (!mapped_args.contains(myTupleArg)) { + myTupleMappedParams.add(tuple_par); + mapped_args.add(myTupleArg); + } + else if (! seen_named_args && tuple_arg_not_exhausted) { + // case of (*(1, 2, 3)) -> (a, *b); map the rest of *arg to *param + myTupleMappedParams.add(tuple_par); + mapped_args.add(myTupleArg); + tuple_arg_not_exhausted = false; + } + } + if (tuple_arg_not_exhausted && ! tuple_dup_found) { + markArgument(myTupleArg, ArgFlag.IS_TOO_LONG); + } + // map unmapped named params to **kwarg + if (myKwdArg != null) { + for (PyParameter par : parameters) { + PyNamedParameter n_par = par.getAsNamed(); + if (n_par != null && !n_par.isKeywordContainer() && !n_par.isPositionalContainer() && slots.get(n_par) == null) { + slots.put(n_par, myKwdArg); + } + } + } + // map unmapped **kwarg to **param + if (myKwdArg != null && kwd_par != null && !mapped_args.contains(myKwdArg)) { + myKwdMappedParams.add(kwd_par); + mapped_args.add(myKwdArg); + } + // fill in ret, mark unmapped named params + for (Map.Entry pair : slots.entrySet()) { + PyNamedParameter n_par = pair.getKey(); + PyExpression arg = pair.getValue(); + if (arg == null) { + if (!n_par.hasDefaultValue()) myUnmappedParams.add(n_par); + } + else { + if (arg == myTupleArg) { + myTupleMappedParams.add(n_par); + } + else if (arg == myKwdArg) { + myKwdMappedParams.add(n_par); + } + else { + myPlainMappedParams.put(arg, n_par); + } + } + } + // mark unmapped args + for (PyExpression arg : slots.values()) { + if (arg != null) mapped_args.add(arg); + } + for (PyExpression arg : arguments) { + if (!mapped_args.contains(arg)) { + final EnumSet flags = myArgFlags.get(arg); + if (flags == null || flags.isEmpty()) { + markArgument(arg, ArgFlag.IS_UNMAPPED); + } + } + } } - } } From 3d2c8f555c505a7643ed9e617563ee1d7679e7a5 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 15:14:40 +0400 Subject: [PATCH 07/24] Evaluate parameter types only when needed --- .../python/psi/types/PyCallableParameter.java | 2 +- .../python/documentation/PyTypeModelBuilder.java | 2 +- .../psi/types/PyCallableParameterImpl.java | 16 ++++++++++++---- .../python/psi/types/PyCallableTypeImpl.java | 2 +- .../python/psi/types/PyFunctionType.java | 3 +-- .../python/psi/types/PyTypeChecker.java | 8 ++++---- .../com/jetbrains/python/PyTypeParserTest.java | 9 +++++---- 7 files changed, 25 insertions(+), 17 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableParameter.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableParameter.java index 9347e22b3d2d..8df290cc9a41 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableParameter.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyCallableParameter.java @@ -12,7 +12,7 @@ public interface PyCallableParameter { String getName(); @Nullable - PyType getType(); + PyType getType(@NotNull TypeEvalContext context); @Nullable PyParameter getParameter(); diff --git a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java index 8a37daa6b275..72d6efc50043 100644 --- a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java @@ -209,7 +209,7 @@ public class PyTypeModelBuilder { if (parameters != null) { parameterModels = new ArrayList(); for (PyCallableParameter parameter : parameters) { - parameterModels.add(new ParamType(parameter.getName(), build(parameter.getType(), true))); + parameterModels.add(new ParamType(parameter.getName(), build(parameter.getType(myContext), true))); } } final PyType ret = type.getCallType(myContext, null); diff --git a/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java b/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java index 2d99cdbf20d6..27dbf1bf8d35 100644 --- a/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java @@ -1,6 +1,8 @@ package com.jetbrains.python.psi.types; +import com.jetbrains.python.psi.PyNamedParameter; import com.jetbrains.python.psi.PyParameter; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -17,9 +19,9 @@ public class PyCallableParameterImpl implements PyCallableParameter { myElement = null; } - public PyCallableParameterImpl(@Nullable PyParameter element, @Nullable PyType type) { + public PyCallableParameterImpl(@Nullable PyParameter element) { myName = null; - myType = type; + myType = null; myElement = element; } @@ -37,8 +39,14 @@ public class PyCallableParameterImpl implements PyCallableParameter { @Nullable @Override - public PyType getType() { - return myType; + public PyType getType(@NotNull TypeEvalContext context) { + if (myType != null) { + return myType; + } + else if (myElement instanceof PyNamedParameter) { + return context.getType((PyNamedParameter)myElement); + } + return null; } @Nullable diff --git a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java index 9e655f9036e9..c1483a2b446b 100644 --- a/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyCallableTypeImpl.java @@ -70,7 +70,7 @@ public class PyCallableTypeImpl implements PyCallableType { if (param != null) { final StringBuilder builder = new StringBuilder(); final String name = param.getName(); - final PyType type = param.getType(); + final PyType type = param.getType(context); if (name != null) { builder.append(name); if (type != null) { diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java index fd3e3a8f4d27..43ef7ddcc3f7 100644 --- a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java @@ -39,8 +39,7 @@ public class PyFunctionType implements PyCallableType { public List getParameters(@NotNull TypeEvalContext context) { final List result = new ArrayList(); for (PyParameter parameter : myCallable.getParameterList().getParameters()) { - final PyType type = parameter instanceof PyTypedElement ? context.getType((PyTypedElement)parameter) : null; - result.add(new PyCallableParameterImpl(parameter, type)); + result.add(new PyCallableParameterImpl(parameter)); } return result; } diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index 92ae7ab35caf..9cd26ffba127 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -152,7 +152,7 @@ public class PyTypeChecker { final PyCallableParameter expectedParam = expectedParameters.get(i); final PyCallableParameter actualParam = actualParameters.get(i); // TODO: Check named and star params, not only positional ones - if (!match(expectedParam.getType(), actualParam.getType(), context, substitutions, recursive)) { + if (!match(expectedParam.getType(context), actualParam.getType(context), context, substitutions, recursive)) { return false; } } @@ -237,7 +237,7 @@ public class PyTypeChecker { if (parameters != null) { for (PyCallableParameter parameter : parameters) { if (parameter != null) { - collectGenerics(parameter.getType(), context, collected, visited); + collectGenerics(parameter.getType(context), context, collected, visited); } } } @@ -284,9 +284,9 @@ public class PyTypeChecker { if (parameters != null) { substParams = new ArrayList(); for (PyCallableParameter parameter : parameters) { - final PyType substType = substitute(parameter.getType(), substitutions, context); + final PyType substType = substitute(parameter.getType(context), substitutions, context); final PyCallableParameter subst = parameter.getParameter() != null ? - new PyCallableParameterImpl(parameter.getParameter(), substType) : + new PyCallableParameterImpl(parameter.getParameter()) : new PyCallableParameterImpl(parameter.getName(), substType); substParams.add(subst); } diff --git a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java index 9407a30d2363..bc1bdc8cb412 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeParserTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeParserTest.java @@ -234,15 +234,16 @@ public class PyTypeParserTest extends PyTestCase { assertInstanceOf(type, PyCallableType.class); final PyCallableType callableType = (PyCallableType)type; assertNotNull(callableType); - final PyType returnType = callableType.getCallType(getTypeEvalContext(), null); + final TypeEvalContext context = getTypeEvalContext(); + final PyType returnType = callableType.getCallType(context, null); assertInstanceOf(returnType, PyGenericType.class); - final List parameterTypes = callableType.getParameters(getTypeEvalContext()); + final List parameterTypes = callableType.getParameters(context); assertNotNull(parameterTypes); assertEquals(2, parameterTypes.size()); - final PyType type0 = parameterTypes.get(0).getType(); + final PyType type0 = parameterTypes.get(0).getType(context); assertNotNull(type0); assertEquals("int", type0.getName()); - final PyType type1 = parameterTypes.get(1).getType(); + final PyType type1 = parameterTypes.get(1).getType(context); assertNotNull(type1); assertEquals("T", type1.getName()); } From dd431693ff16dd26c0058c401075dc2d743269f4 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 16:00:44 +0400 Subject: [PATCH 08/24] Extracted getParameters(Callable, TypeEvalContext) --- .../src/com/jetbrains/python/psi/PyUtil.java | 29 +- .../psi/impl/CallArgumentsMappingImpl.java | 493 +++++++++--------- 2 files changed, 266 insertions(+), 256 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 5ade1e62cdca..e1413a826059 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -1238,5 +1238,32 @@ public class PyUtil { } return element; } -} + @NotNull + public static List getParameters(@NotNull Callable callable, @NotNull TypeEvalContext context) { + PyType type = context.getType(callable); + if (type instanceof PyUnionType) { + type = ((PyUnionType)type).excludeNull(); + } + if (type instanceof PyCallableType) { + final PyCallableType callableType = (PyCallableType)type; + final List callableTypeParameters = callableType.getParameters(context); + if (callableTypeParameters != null) { + boolean allParametersDefined = true; + final List parameters = new ArrayList(); + for (PyCallableParameter callableParameter : callableTypeParameters) { + final PyParameter parameter = callableParameter.getParameter(); + if (parameter == null) { + allParametersDefined = false; + break; + } + parameters.add(parameter); + } + if (allParametersDefined) { + return parameters; + } + } + } + return Arrays.asList(callable.getParameterList().getParameters()); + } +} diff --git a/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java b/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java index 05025e5fb9c6..7122f4c93bc2 100644 --- a/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/CallArgumentsMappingImpl.java @@ -73,280 +73,263 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping { } } } - PyType type = context.getType(myMarkedCallee.getCallable()); - if (type instanceof PyUnionType) { - type = ((PyUnionType)type).excludeNull(); - } - if (type instanceof PyCallableType) { - final PyCallableType callableType = (PyCallableType)type; - final List callableTypeParameters = callableType.getParameters(context); - if (callableTypeParameters != null) { - final List parameters = new ArrayList(); - for (PyCallableParameter callableParameter : callableTypeParameters) { - final PyParameter parameter = callableParameter.getParameter(); - if (parameter == null) { - return; + final List parameters = PyUtil.getParameters(myMarkedCallee.getCallable(), context); + // prepare parameter slots + Map slots = new LinkedHashMap(); + PyNamedParameter kwd_par = null; // **param + PyNamedParameter tuple_par = null; // *param + Set mapped_args = new HashSet(); + final int implicit_offset = resolved_callee.getImplicitOffset(); + int positional_index = 0; // up to this index parameters are positional + // check positional arguments, fill slots + int i = 0; + for (PyParameter par : parameters) { + if (tuple_par == null && kwd_par == null && positional_index < implicit_offset) { + positional_index += 1; + continue; + } + PyNamedParameter n_par = par.getAsNamed(); + if (n_par != null) { + if (n_par.isPositionalContainer()) tuple_par = n_par; + else if (n_par.isKeywordContainer()) kwd_par = n_par; + else { + slots.put(n_par, null); // regular parameter that may serve as positional/named + if (tuple_par == null && kwd_par == null) { + positional_index += 1; // only if we're not past *param / **param } - parameters.add(parameter); } - // prepare parameter slots - Map slots = new LinkedHashMap(); - PyNamedParameter kwd_par = null; // **param - PyNamedParameter tuple_par = null; // *param - Set mapped_args = new HashSet(); - final int implicit_offset = resolved_callee.getImplicitOffset(); - int positional_index = 0; // up to this index parameters are positional - // check positional arguments, fill slots - int i = 0; - for (PyParameter par : parameters) { - if (tuple_par == null && kwd_par == null && positional_index < implicit_offset) { - positional_index += 1; - continue; - } - PyNamedParameter n_par = par.getAsNamed(); - if (n_par != null) { - if (n_par.isPositionalContainer()) tuple_par = n_par; - else if (n_par.isKeywordContainer()) kwd_par = n_par; + } + else { + PyTupleParameter t_par = par.getAsTuple(); + if (t_par != null) positional_index += 1; // tuple can only be positional + // else lone star, skip + } + i += 1; + } + // rule out 'self' or other implicit params + for (i=0; i < implicit_offset && i < parameters.size(); i+=1) { + slots.remove(parameters.get(i).getAsNamed()); + positional_index += 1; + } + // now params to the left of positional_index are positional. + // map positional args to positional params. + // we assume that implicitly skipped parameters are never nested tuples. no idea when they could ever be. + int cnt = implicit_offset; + int positional_bound = arguments.length; // to the right of this pos args are verboten + ListIterator unmatched_arg_iter = unmatched_args.listIterator(); + while (unmatched_arg_iter.hasNext()) { + PyExpression arg = unmatched_arg_iter.next(); + if (arg instanceof PyStarArgument || arg instanceof PyKeywordArgument) { + positional_bound = cnt; + break; + } + if (cnt < parameters.size() && cnt < positional_index) { + final PyParameter par = parameters.get(cnt); + PyNamedParameter n_par = par.getAsNamed(); + if (n_par != null) { + cnt += 1; + slots.put(n_par, PyUtil.peelArgument(arg)); + mapped_args.add(arg); + } + else { + PyTupleParameter t_par = par.getAsTuple(); + if (t_par != null) { + if (arg instanceof PyParenthesizedExpression) { + mapped_args.add(arg); // tuple itself is always mapped; its insides can fail + } else { - slots.put(n_par, null); // regular parameter that may serve as positional/named - if (tuple_par == null && kwd_par == null) { - positional_index += 1; // only if we're not past *param / **param + PyType arg_type = context.getType(arg); + if (arg_type != null && arg_type.isBuiltin(context) && "list".equals(arg_type.getName())) { + mapped_args.add(arg); // we can't really analyze arbitrary lists statically yet + // but ListLiteralExpressions are handled by visitor } } + unmatched_arg_iter.previous(); + MyParamVisitor visitor = new MyParamVisitor(unmatched_arg_iter, this); + visitor.enterTuple(t_par.getAsTuple()); // will recur as needed + unmatched_subargs.addAll(visitor.getUnmatchedSubargs()); // what didn't match inside + cnt += 1; } - else { - PyTupleParameter t_par = par.getAsTuple(); - if (t_par != null) positional_index += 1; // tuple can only be positional - // else lone star, skip + // else: goes to *param + } + } + else break; + } + // anything left after mapping of nested-tuple params? + for (Map.Entry> pair : myNestedMappedParams.entrySet()) { + PyExpression arg = pair.getKey(); + List params = pair.getValue(); + mapped_args.add(arg); + for (PyNamedParameter n_par : params) slots.remove(n_par); + } + for (PyExpression arg : unmatched_subargs) { + markArgument(arg, ArgFlag.IS_UNMAPPED); + } + // mark past-bound positional args + i = positional_bound; + while (i parameter_by_name = new LinkedHashMap(); + for (PyParameter par : parameters) { + PyNamedParameter n_par = par.getAsNamed(); + if (n_par != null) parameter_by_name.put(n_par.getName(), n_par); + } + for (PyExpression arg : arguments) { + if (arg instanceof PyKeywordArgument) { // to explicitly named param? + String arg_name = ((PyKeywordArgument)arg).getKeyword(); + PyNamedParameter respective_par = parameter_by_name.get(arg_name); + if (respective_par != null && !respective_par.isKeywordContainer() && !respective_par.isPositionalContainer()) { + if (slots.get(respective_par) != null) markArgument(arg, ArgFlag.IS_DUP); + else slots.put(respective_par, arg); + } + else { // to **param? + if (kwd_par != null) { + myPlainMappedParams.put(arg, kwd_par); + mapped_args.add(arg); } - i += 1; } - // rule out 'self' or other implicit params - for (i=0; i < implicit_offset && i < parameters.size(); i+=1) { - slots.remove(parameters.get(i).getAsNamed()); - positional_index += 1; - } - // now params to the left of positional_index are positional. - // map positional args to positional params. - // we assume that implicitly skipped parameters are never nested tuples. no idea when they could ever be. - int cnt = implicit_offset; - int positional_bound = arguments.length; // to the right of this pos args are verboten - ListIterator unmatched_arg_iter = unmatched_args.listIterator(); - while (unmatched_arg_iter.hasNext()) { - PyExpression arg = unmatched_arg_iter.next(); - if (arg instanceof PyStarArgument || arg instanceof PyKeywordArgument) { - positional_bound = cnt; + seen_named_args = true; + } + } + // map *arg to positional params if possible + boolean tuple_arg_not_exhausted = false; + boolean tuple_dup_found = false; + if (cnt < parameters.size() && cnt < positional_index && myTupleArg != null) { + // check length of myTupleArg + PyType tuple_arg_type = null; + final PyExpression expression = PsiTreeUtil.getChildOfType(myTupleArg, PyExpression.class); + if (expression != null) { + tuple_arg_type = context.getType(expression); + } + int tuple_length; + boolean tuple_length_known; + if (tuple_arg_type instanceof PyTupleType) { + tuple_length = ((PyTupleType)tuple_arg_type).getElementCount(); + tuple_length_known = true; + } + else { + tuple_length = 2000000; // no practical function will have so many positional params + tuple_length_known = false; + } + int mapped_params_count = 0; + while (cnt < parameters.size() && cnt < positional_index && mapped_params_count < tuple_length) { + PyParameter par = parameters.get(cnt); + if (par instanceof PySingleStarParameter) break; + PyNamedParameter n_par = par.getAsNamed(); + if (slots.containsKey(n_par)) { + final PyExpression arg_here = slots.get(n_par); + if (arg_here != null) { + if (tuple_length_known) { + final EnumSet flags = myArgFlags.get(arg_here); + if (flags == null || flags.isEmpty()) { + markArgument(arg_here, ArgFlag.IS_DUP); + tuple_dup_found = true; + } + } + // else: unknown tuple length is just enough + // the spree is over break; } - if (cnt < parameters.size() && cnt < positional_index) { - final PyParameter par = parameters.get(cnt); - PyNamedParameter n_par = par.getAsNamed(); - if (n_par != null) { - cnt += 1; - slots.put(n_par, PyUtil.peelArgument(arg)); - mapped_args.add(arg); - } - else { - PyTupleParameter t_par = par.getAsTuple(); - if (t_par != null) { - if (arg instanceof PyParenthesizedExpression) { - mapped_args.add(arg); // tuple itself is always mapped; its insides can fail - } - else { - PyType arg_type = context.getType(arg); - if (arg_type != null && arg_type.isBuiltin(context) && "list".equals(arg_type.getName())) { - mapped_args.add(arg); // we can't really analyze arbitrary lists statically yet - // but ListLiteralExpressions are handled by visitor - } - } - unmatched_arg_iter.previous(); - MyParamVisitor visitor = new MyParamVisitor(unmatched_arg_iter, this); - visitor.enterTuple(t_par.getAsTuple()); // will recur as needed - unmatched_subargs.addAll(visitor.getUnmatchedSubargs()); // what didn't match inside - cnt += 1; - } - // else: goes to *param - } + else if (n_par != null) { // normally always true + myTupleMappedParams.add(n_par); + mapped_args.add(myTupleArg); + slots.remove(n_par); } - else break; } - // anything left after mapping of nested-tuple params? - for (Map.Entry> pair : myNestedMappedParams.entrySet()) { - PyExpression arg = pair.getKey(); - List params = pair.getValue(); - mapped_args.add(arg); - for (PyNamedParameter n_par : params) slots.remove(n_par); + else if (n_par == tuple_par) { + mapped_params_count = tuple_length; // we found *param for our *arg, consider it fully mapped + break; } - for (PyExpression arg : unmatched_subargs) { - markArgument(arg, ArgFlag.IS_UNMAPPED); - } - // mark past-bound positional args - i = positional_bound; - while (i parameter_by_name = new LinkedHashMap(); - for (PyParameter par : parameters) { - PyNamedParameter n_par = par.getAsNamed(); - if (n_par != null) parameter_by_name.put(n_par.getName(), n_par); + } + } + // map unmapped *arg to *param + if (myTupleArg != null && tuple_par != null) { + if (!mapped_args.contains(myTupleArg)) { + myTupleMappedParams.add(tuple_par); + mapped_args.add(myTupleArg); + } + else if (! seen_named_args && tuple_arg_not_exhausted) { + // case of (*(1, 2, 3)) -> (a, *b); map the rest of *arg to *param + myTupleMappedParams.add(tuple_par); + mapped_args.add(myTupleArg); + tuple_arg_not_exhausted = false; + } + } + if (tuple_arg_not_exhausted && ! tuple_dup_found) { + markArgument(myTupleArg, ArgFlag.IS_TOO_LONG); + } + // map unmapped named params to **kwarg + if (myKwdArg != null) { + for (PyParameter par : parameters) { + PyNamedParameter n_par = par.getAsNamed(); + if (n_par != null && !n_par.isKeywordContainer() && !n_par.isPositionalContainer() && slots.get(n_par) == null) { + slots.put(n_par, myKwdArg); } - for (PyExpression arg : arguments) { - if (arg instanceof PyKeywordArgument) { // to explicitly named param? - String arg_name = ((PyKeywordArgument)arg).getKeyword(); - PyNamedParameter respective_par = parameter_by_name.get(arg_name); - if (respective_par != null && !respective_par.isKeywordContainer() && !respective_par.isPositionalContainer()) { - if (slots.get(respective_par) != null) markArgument(arg, ArgFlag.IS_DUP); - else slots.put(respective_par, arg); - } - else { // to **param? - if (kwd_par != null) { - myPlainMappedParams.put(arg, kwd_par); - mapped_args.add(arg); - } - } - seen_named_args = true; - } + } + } + // map unmapped **kwarg to **param + if (myKwdArg != null && kwd_par != null && !mapped_args.contains(myKwdArg)) { + myKwdMappedParams.add(kwd_par); + mapped_args.add(myKwdArg); + } + // fill in ret, mark unmapped named params + for (Map.Entry pair : slots.entrySet()) { + PyNamedParameter n_par = pair.getKey(); + PyExpression arg = pair.getValue(); + if (arg == null) { + if (!n_par.hasDefaultValue()) myUnmappedParams.add(n_par); + } + else { + if (arg == myTupleArg) { + myTupleMappedParams.add(n_par); } - // map *arg to positional params if possible - boolean tuple_arg_not_exhausted = false; - boolean tuple_dup_found = false; - if (cnt < parameters.size() && cnt < positional_index && myTupleArg != null) { - // check length of myTupleArg - PyType tuple_arg_type = null; - final PyExpression expression = PsiTreeUtil.getChildOfType(myTupleArg, PyExpression.class); - if (expression != null) { - tuple_arg_type = context.getType(expression); - } - int tuple_length; - boolean tuple_length_known; - if (tuple_arg_type instanceof PyTupleType) { - tuple_length = ((PyTupleType)tuple_arg_type).getElementCount(); - tuple_length_known = true; - } - else { - tuple_length = 2000000; // no practical function will have so many positional params - tuple_length_known = false; - } - int mapped_params_count = 0; - while (cnt < parameters.size() && cnt < positional_index && mapped_params_count < tuple_length) { - PyParameter par = parameters.get(cnt); - if (par instanceof PySingleStarParameter) break; - PyNamedParameter n_par = par.getAsNamed(); - if (slots.containsKey(n_par)) { - final PyExpression arg_here = slots.get(n_par); - if (arg_here != null) { - if (tuple_length_known) { - final EnumSet flags = myArgFlags.get(arg_here); - if (flags == null || flags.isEmpty()) { - markArgument(arg_here, ArgFlag.IS_DUP); - tuple_dup_found = true; - } - } - // else: unknown tuple length is just enough - // the spree is over - break; - } - else if (n_par != null) { // normally always true - myTupleMappedParams.add(n_par); - mapped_args.add(myTupleArg); - slots.remove(n_par); - } - } - else if (n_par == tuple_par) { - mapped_params_count = tuple_length; // we found *param for our *arg, consider it fully mapped - break; - } - cnt += 1; - mapped_params_count += 1; - } - if ( - tuple_length_known && (mapped_params_count < tuple_length) || // not exhausted - mapped_params_count == 0 // unknown length must consume at least first param - ) { - tuple_arg_not_exhausted = true; - } + else if (arg == myKwdArg) { + myKwdMappedParams.add(n_par); } - // map *param to the leftmost chunk of unmapped positional args - // NOTE: ignores the structure of nested-tuple params! - if (tuple_par != null) { - i = 0; - while (i < arguments.length && mapped_args.contains(arguments[i]) && isPositionalArg(arguments[i])) { - i += 1; // skip first mapped args - } - if (i < arguments.length && isPositionalArg(arguments[i])) { - while (i < arguments.length && !mapped_args.contains(arguments[i]) && isPositionalArg(arguments[i])) { - myPlainMappedParams.put(arguments[i], tuple_par); - mapped_args.add(arguments[i]); - i += 1; - } - } + else { + myPlainMappedParams.put(arg, n_par); } - // map unmapped *arg to *param - if (myTupleArg != null && tuple_par != null) { - if (!mapped_args.contains(myTupleArg)) { - myTupleMappedParams.add(tuple_par); - mapped_args.add(myTupleArg); - } - else if (! seen_named_args && tuple_arg_not_exhausted) { - // case of (*(1, 2, 3)) -> (a, *b); map the rest of *arg to *param - myTupleMappedParams.add(tuple_par); - mapped_args.add(myTupleArg); - tuple_arg_not_exhausted = false; - } - } - if (tuple_arg_not_exhausted && ! tuple_dup_found) { - markArgument(myTupleArg, ArgFlag.IS_TOO_LONG); - } - // map unmapped named params to **kwarg - if (myKwdArg != null) { - for (PyParameter par : parameters) { - PyNamedParameter n_par = par.getAsNamed(); - if (n_par != null && !n_par.isKeywordContainer() && !n_par.isPositionalContainer() && slots.get(n_par) == null) { - slots.put(n_par, myKwdArg); - } - } - } - // map unmapped **kwarg to **param - if (myKwdArg != null && kwd_par != null && !mapped_args.contains(myKwdArg)) { - myKwdMappedParams.add(kwd_par); - mapped_args.add(myKwdArg); - } - // fill in ret, mark unmapped named params - for (Map.Entry pair : slots.entrySet()) { - PyNamedParameter n_par = pair.getKey(); - PyExpression arg = pair.getValue(); - if (arg == null) { - if (!n_par.hasDefaultValue()) myUnmappedParams.add(n_par); - } - else { - if (arg == myTupleArg) { - myTupleMappedParams.add(n_par); - } - else if (arg == myKwdArg) { - myKwdMappedParams.add(n_par); - } - else { - myPlainMappedParams.put(arg, n_par); - } - } - } - // mark unmapped args - for (PyExpression arg : slots.values()) { - if (arg != null) mapped_args.add(arg); - } - for (PyExpression arg : arguments) { - if (!mapped_args.contains(arg)) { - final EnumSet flags = myArgFlags.get(arg); - if (flags == null || flags.isEmpty()) { - markArgument(arg, ArgFlag.IS_UNMAPPED); - } - } + } + } + // mark unmapped args + for (PyExpression arg : slots.values()) { + if (arg != null) mapped_args.add(arg); + } + for (PyExpression arg : arguments) { + if (!mapped_args.contains(arg)) { + final EnumSet flags = myArgFlags.get(arg); + if (flags == null || flags.isEmpty()) { + markArgument(arg, ArgFlag.IS_UNMAPPED); } } } From ab034cf35a5eb0b776248e9e21ba0518ca9e4867 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 16:04:27 +0400 Subject: [PATCH 09/24] Override function signatures in user skeletons --- .../com/jetbrains/python/psi/impl/PyTypeProvider.java | 3 +++ .../jetbrains/python/psi/types/PyTypeProviderBase.java | 6 ++++++ .../userSkeletons/PyUserSkeletonsTypeProvider.java | 10 ++++++++++ .../com/jetbrains/python/psi/impl/PyFunctionImpl.java | 6 ++++++ .../python/psi/impl/PyLambdaExpressionImpl.java | 7 +++++++ 5 files changed, 32 insertions(+) diff --git a/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java b/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java index 38dfcac02dae..cc3452d52dc6 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java +++ b/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java @@ -31,4 +31,7 @@ public interface PyTypeProvider { @Nullable PyType getContextManagerVariableType(PyClass contextManager, PyExpression withExpression, TypeEvalContext context); + + @Nullable + PyType getCallableType(@NotNull Callable callable, @NotNull TypeEvalContext context); } diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java index 81988147bd01..aa23c396bab6 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java @@ -102,6 +102,12 @@ public class PyTypeProviderBase implements PyTypeProvider { return null; } + @Nullable + @Override + public PyType getCallableType(@NotNull Callable callable, @NotNull TypeEvalContext context) { + return null; + } + protected void registerSelfReturnType(String classQualifiedName, Collection methods) { registerReturnType(classQualifiedName, methods, mySelfTypeCallback); } diff --git a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java index f93b3b22978c..0a7d129b0018 100644 --- a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsTypeProvider.java @@ -46,4 +46,14 @@ public class PyUserSkeletonsTypeProvider extends PyTypeProviderBase { } return null; } + + @Nullable + @Override + public PyType getCallableType(@NotNull Callable callable, @NotNull TypeEvalContext context) { + final Callable callableSkeleton = PyUserSkeletonsUtil.getUserSkeleton(callable); + if (callableSkeleton != null) { + return context.getType(callableSkeleton); + } + return null; + } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 81392e382669..310641880a04 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -342,6 +342,12 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp @Override public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { + for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { + final PyType type = provider.getCallableType(this, context); + if (type != null) { + return type; + } + } final PyFunctionType type = new PyFunctionType(this); if (getDecoratorList() != null) { return PyUnionType.createWeakType(type); diff --git a/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java index fc31ce2e21a3..9bf0de63425e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java @@ -1,6 +1,7 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; +import com.intellij.openapi.extensions.Extensions; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; @@ -24,6 +25,12 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp } public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { + for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) { + final PyType type = provider.getCallableType(this, context); + if (type != null) { + return type; + } + } return new PyFunctionType(this); } From a77dcfc4256536e0b08ed66d666aae1e7d62c73d Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 18:34:55 +0400 Subject: [PATCH 10/24] Use parameters from function type in documentation pop-ups --- .../PythonDocumentationProvider.java | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java index 9a040841ae74..6105abf34a2b 100644 --- a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java +++ b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java @@ -15,9 +15,11 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.Function; import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.console.PydevConsoleRunner; @@ -122,7 +124,19 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i ChainIterable cat = new ChainIterable(); final String name = fun.getName(); cat.addItem("def ").addWith(func_name_wrapper, $(name)); - cat.addItem(escaper.apply(PyUtil.getReadableRepr(fun.getParameterList(), false))); + final TypeEvalContext context = TypeEvalContext.userInitiated(fun.getContainingFile()); + final List parameters = PyUtil.getParameters(fun, context); + final String paramStr = "(" + + StringUtil.join(parameters, + new Function() { + @Override + public String fun(PyParameter parameter) { + return PyUtil.getReadableRepr(parameter, false); + } + }, + ", ") + + ")"; + cat.addItem(escaper.apply(paramStr)); if (!PyNames.INIT.equals(name)) { cat.addItem(escaper.apply("\nInferred type: ")); getTypeDescription(fun, cat); @@ -157,16 +171,10 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i return String.format("Inferred type: %s", getTypeName(context.getType(element), context)); } - public static String getTypeDescription(@NotNull PyFunction fun) { - final TypeEvalContext context = TypeEvalContext.userInitiated(fun.getContainingFile()); - PyTypeModelBuilder builder = new PyTypeModelBuilder(context); - return builder.build(fun).asString(); - } - public static void getTypeDescription(@NotNull PyFunction fun, ChainIterable body) { final TypeEvalContext context = TypeEvalContext.userInitiated(fun.getContainingFile()); PyTypeModelBuilder builder = new PyTypeModelBuilder(context); - builder.build(fun).toBodyWithLinks(body, fun); + builder.build(context.getType(fun), true).toBodyWithLinks(body, fun); } public static String getTypeName(@Nullable PyType type, @NotNull final TypeEvalContext context) { @@ -552,27 +560,28 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i private static String generateContent(PyFunction function, String offset, String prefix, boolean checkReturn) { //TODO: this code duplicates PyDocstringGenerator in some parts - PyParameter[] list = function.getParameterList().getParameters(); - StringBuilder builder = new StringBuilder(offset); + final StringBuilder builder = new StringBuilder(offset); + final TypeEvalContext context = TypeEvalContext.userInitiated(function.getContainingFile()); PySignature signature = PySignatureCacheManager.getInstance(function.getProject()).findSignature(function); - for (PyParameter p : list) { - if (p.getText().equals(PyNames.CANONICAL_SELF) || p.getName() == null) { + for (PyParameter p : PyUtil.getParameters(function, context)) { + final String parameterName = p.getName(); + if (p.getText().equals(PyNames.CANONICAL_SELF) || parameterName == null) { continue; } - String argType = signature == null ? null : signature.getArgTypeQualifiedName(p.getName()); + String argType = signature == null ? null : signature.getArgTypeQualifiedName(parameterName); if (argType == null) { builder.append(prefix); builder.append("param "); - builder.append(p.getName()); + builder.append(parameterName); builder.append(": "); builder.append(offset); } if (PyCodeInsightSettings.getInstance().INSERT_TYPE_DOCSTUB || argType != null) { builder.append(prefix); builder.append("type "); - builder.append(p.getName()); + builder.append(parameterName); builder.append(": "); if (signature != null) { builder.append(PySignatureUtil.getShortestImportableName(function, argType)); From d7fad244489cfb0dce67c3bd4f93bd2f18246cbe Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 18:35:34 +0400 Subject: [PATCH 11/24] Removed unused method --- .../documentation/PyTypeModelBuilder.java | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java index 72d6efc50043..9849f3843d53 100644 --- a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java @@ -2,13 +2,9 @@ package com.jetbrains.python.documentation; import com.google.common.base.Function; import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.intellij.psi.PsiElement; import com.jetbrains.python.PyNames; -import com.jetbrains.python.psi.Callable; -import com.jetbrains.python.psi.PyNamedParameter; -import com.jetbrains.python.psi.PyParameter; import com.jetbrains.python.psi.types.*; import com.jetbrains.python.toolbox.ChainIterable; import org.jetbrains.annotations.NotNull; @@ -217,27 +213,6 @@ public class PyTypeModelBuilder { return new FunctionType(returnType, parameterModels); } - public TypeModel build(Callable callable) { - final PyType returnType = callable.getReturnType(myContext, null); - return new FunctionType(build(returnType, true), Collections2.transform(Lists.newArrayList(callable.getParameterList().getParameters()), - new Function() { - @Override - public TypeModel apply(PyParameter p) { - final PyNamedParameter np = p.getAsNamed(); - if (np != null) { - TypeModel paramType = _(PyNames.UNKNOWN_TYPE); - final PyType t = myContext.getType(np); - if (t != null) { - paramType = build(t, true); - } - final String name = PyFunctionType.getParameterName(np); - return new ParamType(name, paramType); - } - return new ParamType(p.toString(), null); - } - })); - } - private interface TypeVisitor { void oneOf(OneOf oneOf); From 57068612cb96b28fb29fc1c181b56cbb01f2d781 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 18:36:15 +0400 Subject: [PATCH 12/24] Use parameters from function type in parameter info pop-up --- .../jetbrains/python/PyParameterInfoHandler.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/python/src/com/jetbrains/python/PyParameterInfoHandler.java b/python/src/com/jetbrains/python/PyParameterInfoHandler.java index 9cff15d75fc2..ff567ffaa17f 100644 --- a/python/src/com/jetbrains/python/PyParameterInfoHandler.java +++ b/python/src/com/jetbrains/python/PyParameterInfoHandler.java @@ -10,6 +10,7 @@ import com.intellij.util.text.CharArrayUtil; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.ParamHelper; import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -110,12 +111,14 @@ public class PyParameterInfoHandler implements ParameterInfoHandler parameterList = Arrays.asList(callable.getParameterList().getParameters()); + final List parameterList = PyUtil.getParameters(callable, typeEvalContext); final List namedParameters = new ArrayList(parameterList.size()); // param -> hint index. indexes are not contiguous, because some hints are parentheses. @@ -123,7 +126,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler flags. this includes flags for parens. final Map> hintFlags = new HashMap>(); - final List hintsList = buildParameterListHint(callable, namedParameters, parameterToIndex, hintFlags); + final List hintsList = buildParameterListHint(parameterList, namedParameters, parameterToIndex, hintFlags); final int currentParamOffset = context.getCurrentParameterIndex(); // in Python mode, we get an offset here, not an index! @@ -268,19 +271,18 @@ public class PyParameterInfoHandler implements ParameterInfoHandler buildParameterListHint(@NotNull final Callable callable, + private static List buildParameterListHint(@NotNull List parameters, @NotNull final List namedParameters, @NotNull final Map parameterToIndex, @NotNull final Map> hintFlags) { - final List hintsList = new ArrayList(); ParamHelper.walkDownParamArray( - callable.getParameterList().getParameters(), + parameters.toArray(new PyParameter[parameters.size()]), new ParamHelper.ParamWalker() { public void enterTupleParameter(PyTupleParameter param, boolean first, boolean last) { hintFlags.put(hintsList.size(), EnumSet.noneOf(ParameterInfoUIContextEx.Flag.class)); From c483f6767b7af3bd0957fc4c57f5d7c3d1d01dc9 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 18:36:53 +0400 Subject: [PATCH 13/24] Use parameters from function type in keyword completion --- .../psi/impl/references/KeywordArgumentCompletionUtil.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/psi/impl/references/KeywordArgumentCompletionUtil.java b/python/src/com/jetbrains/python/psi/impl/references/KeywordArgumentCompletionUtil.java index c97a4deffba3..e018a99d541f 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/KeywordArgumentCompletionUtil.java +++ b/python/src/com/jetbrains/python/psi/impl/references/KeywordArgumentCompletionUtil.java @@ -11,6 +11,7 @@ import com.jetbrains.python.psi.impl.PyKeywordArgumentProvider; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.QualifiedResolveResult; import com.jetbrains.python.psi.search.PySuperMethodsSearch; +import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -50,7 +51,11 @@ public class KeywordArgumentCompletionUtil { visited.add(def); boolean needSelf = def.getContainingClass() != null && def.getModifier() != PyFunction.Modifier.STATICMETHOD; final KwArgParameterCollector collector = new KwArgParameterCollector(needSelf, ret); - def.getParameterList().acceptChildren(collector); + final TypeEvalContext context = TypeEvalContext.userInitiated(def.getContainingFile()); + final List parameters = PyUtil.getParameters(def, context); + for (PyParameter parameter : parameters) { + parameter.accept(collector); + } if (collector.hasKwArgs()) { for (PyKeywordArgumentProvider provider : Extensions.getExtensions(PyKeywordArgumentProvider.EP_NAME)) { final List arguments = provider.getKeywordArguments(def, callExpr); From 5b7dbb4811ef78a5387468cccccab6af553929f8 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 18:38:50 +0400 Subject: [PATCH 14/24] Use parameters from function type when inspecting function calls --- .../inspections/PyArgumentListInspection.java | 19 ++++++++++--------- .../inspections/PyCallByClassInspection.java | 7 ++++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java index 3c40839d3853..b6c5b7be30a2 100644 --- a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java @@ -57,16 +57,17 @@ public class PyArgumentListInspection extends PyInspection { if (mkfunc != null && !mkfunc.isImplicitlyResolved()) { Callable callable = mkfunc.getCallable(); int first_param_offset = mkfunc.getImplicitOffset(); - PyParameter[] params = callable.getParameterList().getParameters(); - PyNamedParameter alleged_first_param = params.length < first_param_offset ? null : params[first_param_offset-1].getAsNamed(); + final List params = PyUtil.getParameters(callable, myTypeEvalContext); + final PyNamedParameter alleged_first_param = params.size() < first_param_offset ? + null : params.get(first_param_offset-1).getAsNamed(); if (alleged_first_param == null || alleged_first_param.isKeywordContainer()) { // no parameters left to pass function implicitly, or wrong param type registerProblem(deco, PyBundle.message("INSP.func.$0.lacks.first.arg", callable.getName())); // TODO: better names for anon lambdas } else { // possible unfilled params - for (int i=first_param_offset; i < params.length; i += 1) { - PyNamedParameter par = params[i].getAsNamed(); + for (int i=first_param_offset; i < params.size(); i += 1) { + PyNamedParameter par = params.get(i).getAsNamed(); // param tuples, non-starred or non-default won't do if (par == null || (! par.isKeywordContainer() && ! par.isPositionalContainer() && !par.hasDefaultValue())) { String par_name; @@ -87,7 +88,7 @@ public class PyArgumentListInspection extends PyInspection { public static void inspectPyArgumentList(PyArgumentList node, ProblemsHolder holder, final TypeEvalContext context, int implicitOffset) { if (node.getParent() instanceof PyClass) return; // class Foo(object) is also an arg list CallArgumentsMapping result = node.analyzeCall(PyResolveContext.noImplicits().withTypeEvalContext(context), implicitOffset); - highlightIncorrectArguments(holder, result); + highlightIncorrectArguments(holder, result, context); highlightMissingArguments(node, holder, result); highlightStarArgumentTypeMismatch(node, holder, context); } @@ -96,7 +97,7 @@ public class PyArgumentListInspection extends PyInspection { inspectPyArgumentList(node, holder, context, 0); } - private static void highlightIncorrectArguments(ProblemsHolder holder, CallArgumentsMapping result) { + private static void highlightIncorrectArguments(ProblemsHolder holder, CallArgumentsMapping result, @NotNull TypeEvalContext context) { for (Map.Entry> argEntry : result.getArgumentFlags().entrySet()) { EnumSet flags = argEntry.getValue(); if (!flags.isEmpty()) { // something's wrong @@ -120,9 +121,9 @@ public class PyArgumentListInspection extends PyInspection { final PyCallExpression.PyMarkedCallee markedCallee = result.getMarkedCallee(); String parameterName = null; if (markedCallee != null) { - final PyParameter[] parameters = markedCallee.getCallable().getParameterList().getParameters(); - for (int i = parameters.length - 1; i >= 0; --i) { - final PyParameter param = parameters[i]; + final List parameters = PyUtil.getParameters(markedCallee.getCallable(), context); + for (int i = parameters.size() - 1; i >= 0; --i) { + final PyParameter param = parameters.get(i); if (param instanceof PyNamedParameter) { final List unmappedParams = result.getUnmappedParams(); if (!((PyNamedParameter)param).isPositionalContainer() && !((PyNamedParameter)param).isKeywordContainer() && diff --git a/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java b/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java index 72aca8cbd9a2..b2d557941f7b 100644 --- a/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java @@ -12,6 +12,7 @@ import com.jetbrains.python.psi.types.PyType; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import java.util.List; import java.util.Map; import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD; @@ -77,9 +78,9 @@ public class PyCallByClassInspection extends PyInspection { CallArgumentsMapping analysis = arglist.analyzeCall(resolveWithoutImplicits()); final PyCallExpression.PyMarkedCallee markedCallee = analysis.getMarkedCallee(); if (markedCallee != null && markedCallee.getModifier() != STATICMETHOD) { - PyParameter[] params = markedCallee.getCallable().getParameterList().getParameters(); - if (params.length > 0 && params[0] instanceof PyNamedParameter) { - PyNamedParameter first_param = (PyNamedParameter)params[0]; + final List params = PyUtil.getParameters(markedCallee.getCallable(), myTypeEvalContext); + if (params.size() > 0 && params.get(0) instanceof PyNamedParameter) { + PyNamedParameter first_param = (PyNamedParameter)params.get(0); for (Map.Entry entry : analysis.getPlainMappedParams().entrySet()) { // we ignore *arg and **arg which we cannot analyze if (entry.getValue() == first_param) { From 3d03c8954df74094c0c5c44dc5b5fba88b08c7d7 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 18:59:27 +0400 Subject: [PATCH 15/24] Fixed signatures of slice() and xrange() builtins (PY-9978) --- .../inspections/PyArgumentListInspection/slice.py | 5 +++++ .../inspections/PyArgumentListInspection/xRange.py | 5 +++++ .../inspections/PyArgumentListInspectionTest.java | 10 ++++++++++ 3 files changed, 20 insertions(+) create mode 100644 python/testData/inspections/PyArgumentListInspection/slice.py create mode 100644 python/testData/inspections/PyArgumentListInspection/xRange.py diff --git a/python/testData/inspections/PyArgumentListInspection/slice.py b/python/testData/inspections/PyArgumentListInspection/slice.py new file mode 100644 index 000000000000..486eb2ecd158 --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/slice.py @@ -0,0 +1,5 @@ +print(slice()) +print(slice(1)) +print(slice(1, 2)) +print(slice(1, 2, 3)) +print(slice(1, 2, 3, 4)) diff --git a/python/testData/inspections/PyArgumentListInspection/xRange.py b/python/testData/inspections/PyArgumentListInspection/xRange.py new file mode 100644 index 000000000000..2eaede119bad --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/xRange.py @@ -0,0 +1,5 @@ +print(xrange()) +print(xrange(1)) +print(xrange(1, 2)) +print(xrange(1, 2, 3)) +print(xrange(1, 2, 3, 4)) diff --git a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java index 0454cd0d322f..e8b9a9797884 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java @@ -103,6 +103,16 @@ public class PyArgumentListInspectionTest extends PyTestCase { doTest(); } + // PY-9978 + public void testXRange() { + doTest(); + } + + // PY-9978 + public void testSlice() { + doTest(); + } + public void testPy3k() { PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON30); try { From 46ebd5793b06fb4ee4d1d49f2b7fa2880196f813 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 20:13:42 +0400 Subject: [PATCH 16/24] Converted anonymous classes to static inner classes --- .../FunctionalParserBase.java | 329 +++++++++++------- 1 file changed, 204 insertions(+), 125 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/types/functionalParser/FunctionalParserBase.java b/python/src/com/jetbrains/python/psi/types/functionalParser/FunctionalParserBase.java index c8c571764d5f..f7420b849b18 100644 --- a/python/src/com/jetbrains/python/psi/types/functionalParser/FunctionalParserBase.java +++ b/python/src/com/jetbrains/python/psi/types/functionalParser/FunctionalParserBase.java @@ -35,46 +35,12 @@ public abstract class FunctionalParserBase implements FunctionalParser FunctionalParser, T> token(@NotNull final T type, @Nullable final String text) { - return new FunctionalParserBase, T>() { - @NotNull - @Override - public Pair, State> parse(@NotNull List> tokens, @NotNull State state) throws ParserException { - final int pos = state.getPos(); - if (pos >= tokens.size()) { - throw new ParserException("No tokens left", state); - } - final Token token = tokens.get(pos); - if (token.getType().equals(type) && (text == null || token.getText().equals(text))) { - final int newPos = pos + 1; - final State newState = new State(state, newPos, Math.max(newPos, state.getMax())); - return Pair.create(token, newState); - } - final String expected = text != null ? String.format("Token(<%s>, \"%s\")", type, text) : String.format("Token(<%s>)", type); - throw new ParserException(String.format("Expected %s, found %s", expected, token), state); - } - }; + return new TokenParser(type, text); } @NotNull public static FunctionalParser, T> many(@NotNull final FunctionalParser parser) { - return new FunctionalParserBase, T>() { - @NotNull - @Override - public Pair, State> parse(@NotNull List> tokens, @NotNull State state) throws ParserException { - final List list = new ArrayList(); - try { - //noinspection InfiniteLoopStatement - while (true) { - final Pair result = parser.parse(tokens, state); - state = result.getSecond(); - list.add(result.getFirst()); - } - } - catch (ParserException e) { - return Pair.create(list, new State(state, state.getPos(), e.getState().getMax())); - } - } - }; + return new ManyParser(parser); } @NotNull @@ -98,45 +64,13 @@ public abstract class FunctionalParserBase implements FunctionalParser cached() { - final FunctionalParser thisParser = this; - return new FunctionalParserBase() { - private Object myKey = null; - private Map>> myCache = new HashMap>>(); - - @NotNull - @Override - public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { - if (myKey != state.getKey()) { - myKey = state.getKey(); - myCache.clear(); - } - final SoftReference> ref = myCache.get(state.getPos()); - if (ref != null) { - final Pair cached = ref.get(); - if (cached != null) { - return cached; - } - } - final Pair result = thisParser.parse(tokens, state); - myCache.put(state.getPos(), new SoftReference>(result)); - return result; - } - }; + return new CachedParser(this); } @NotNull @Override public FunctionalParser, T> then(@NotNull final FunctionalParser parser) { - final FunctionalParser thisParser = this; - return new FunctionalParserBase, T>() { - @NotNull - @Override - public Pair, State> parse(@NotNull List> tokens, @NotNull State state) throws ParserException { - final Pair result1 = thisParser.parse(tokens, state); - final Pair result2 = parser.parse(tokens, result1.getSecond()); - return Pair.create(Pair.create(result1.getFirst(), result2.getFirst()), result2.getSecond()); - } - }; + return new ThenParser(this, parser); } @NotNull @@ -154,82 +88,227 @@ public abstract class FunctionalParserBase implements FunctionalParser or(@NotNull final FunctionalParser parser) { - final FunctionalParserBase thisParser = this; - return new FunctionalParserBase() { - @NotNull - @Override - public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { - try { - return thisParser.parse(tokens, state); - } - catch (ParserException e) { - return parser.parse(tokens, new State(state, state.getPos(), e.getState().getMax())); - } - } - }; + return new OrParser(this, parser); } @NotNull @Override public FunctionalParser map(@NotNull final Function f) { - final FunctionalParserBase thisParser = this; - return new FunctionalParserBase() { - @NotNull - @Override - public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { - final Pair result = thisParser.parse(tokens, state); - return Pair.create(f.fun(result.getFirst()), result.getSecond()); - } - }; + return new MapParser(this, f); } @NotNull private static FunctionalParser first(@NotNull final FunctionalParser, T> parser) { - return new FunctionalParserBase() { - @NotNull - @Override - public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { - final Pair, State> result = parser.parse(tokens, state); - return Pair.create(result.getFirst().getFirst(), result.getSecond()); - } - }; + return new FirstParser(parser); } @NotNull private static FunctionalParser second(@NotNull final FunctionalParser, T> parser) { - return new FunctionalParserBase() { - @NotNull - @Override - public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { - final Pair, State> result = parser.parse(tokens, state); - return Pair.create(result.getFirst().getSecond(), result.getSecond()); - } - }; + return new SecondParser(parser); } @NotNull private static FunctionalParser finished() { - return new FunctionalParserBase() { - @NotNull - @Override - public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { - final int pos = state.getPos(); - if (pos >= tokens.size()) { - return Pair.create(null, state); - } - throw new ParserException(String.format("Expected end of input, found %s", tokens.get(pos)), state); - } - }; + return new FinishedParser(); } @NotNull private static FunctionalParser pure(@Nullable final R value) { - return new FunctionalParserBase() { - @NotNull - @Override - public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { - return Pair.create(value, state); + return new PureParser(value); + } + + private static class TokenParser extends FunctionalParserBase, T> { + @NotNull private final T myType; + @Nullable private final String myText; + + public TokenParser(@NotNull T type, @Nullable String text) { + myType = type; + myText = text; + } + + @NotNull + @Override + public Pair, State> parse(@NotNull List> tokens, @NotNull State state) throws ParserException { + final int pos = state.getPos(); + if (pos >= tokens.size()) { + throw new ParserException("No tokens left", state); } - }; + final Token token = tokens.get(pos); + if (token.getType().equals(myType) && (myText == null || token.getText().equals(myText))) { + final int newPos = pos + 1; + final State newState = new State(state, newPos, Math.max(newPos, state.getMax())); + return Pair.create(token, newState); + } + final String expected = myText != null ? String.format("Token(<%s>, \"%s\")", myType, myText) : String.format("Token(<%s>)", myType); + throw new ParserException(String.format("Expected %s, found %s", expected, token), state); + } + } + + private static class ManyParser extends FunctionalParserBase, T> { + @NotNull private final FunctionalParser myParser; + + public ManyParser(@NotNull FunctionalParser parser) { + myParser = parser; + } + + @NotNull + @Override + public Pair, State> parse(@NotNull List> tokens, @NotNull State state) throws ParserException { + final List list = new ArrayList(); + try { + //noinspection InfiniteLoopStatement + while (true) { + final Pair result = myParser.parse(tokens, state); + state = result.getSecond(); + list.add(result.getFirst()); + } + } + catch (ParserException e) { + return Pair.create(list, new State(state, state.getPos(), e.getState().getMax())); + } + } + } + + private static class CachedParser extends FunctionalParserBase { + @NotNull private final FunctionalParser myParser; + @Nullable private Object myKey; + @NotNull private Map>> myCache; + + public CachedParser(@NotNull FunctionalParser parser) { + myParser = parser; + myKey = null; + myCache = new HashMap>>(); + } + + @NotNull + @Override + public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { + if (myKey != state.getKey()) { + myKey = state.getKey(); + myCache.clear(); + } + final SoftReference> ref = myCache.get(state.getPos()); + if (ref != null) { + final Pair cached = ref.get(); + if (cached != null) { + return cached; + } + } + final Pair result = myParser.parse(tokens, state); + myCache.put(state.getPos(), new SoftReference>(result)); + return result; + } + } + + private static class OrParser extends FunctionalParserBase { + @NotNull private final FunctionalParserBase myFirst; + @NotNull private final FunctionalParser mySecond; + + public OrParser(@NotNull FunctionalParserBase first, @NotNull FunctionalParser second) { + myFirst = first; + mySecond = second; + } + + @NotNull + @Override + public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { + try { + return myFirst.parse(tokens, state); + } + catch (ParserException e) { + return mySecond.parse(tokens, new State(state, state.getPos(), e.getState().getMax())); + } + } + } + + private static class FirstParser extends FunctionalParserBase { + @NotNull private final FunctionalParser, T> myParser; + + public FirstParser(@NotNull FunctionalParser, T> parser) { + myParser = parser; + } + + @NotNull + @Override + public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { + final Pair, State> result = myParser.parse(tokens, state); + return Pair.create(result.getFirst().getFirst(), result.getSecond()); + } + } + + private static class SecondParser extends FunctionalParserBase { + @NotNull private final FunctionalParser, T> myParser; + + public SecondParser(@NotNull FunctionalParser, T> parser) { + myParser = parser; + } + + @NotNull + @Override + public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { + final Pair, State> result = myParser.parse(tokens, state); + return Pair.create(result.getFirst().getSecond(), result.getSecond()); + } + } + + private static class FinishedParser extends FunctionalParserBase { + @NotNull + @Override + public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { + final int pos = state.getPos(); + if (pos >= tokens.size()) { + return Pair.create(null, state); + } + throw new ParserException(String.format("Expected end of input, found %s", tokens.get(pos)), state); + } + } + + private static class PureParser extends FunctionalParserBase { + @Nullable private final R myValue; + + public PureParser(@Nullable R value) { + myValue = value; + } + + @NotNull + @Override + public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { + return Pair.create(myValue, state); + } + } + + private static class ThenParser extends FunctionalParserBase, T> { + @NotNull private final FunctionalParser myFirst; + @NotNull private final FunctionalParser mySecond; + + public ThenParser(@NotNull FunctionalParser first, @NotNull FunctionalParser second) { + myFirst = first; + mySecond = second; + } + + @NotNull + @Override + public Pair, State> parse(@NotNull List> tokens, @NotNull State state) throws ParserException { + final Pair result1 = myFirst.parse(tokens, state); + final Pair result2 = mySecond.parse(tokens, result1.getSecond()); + return Pair.create(Pair.create(result1.getFirst(), result2.getFirst()), result2.getSecond()); + } + } + + private static class MapParser extends FunctionalParserBase { + @NotNull private final FunctionalParserBase myParser; + @NotNull private final Function myFunction; + + public MapParser(@NotNull FunctionalParserBase parser, @NotNull Function function) { + myParser = parser; + myFunction = function; + } + + @NotNull + @Override + public Pair parse(@NotNull List> tokens, @NotNull State state) throws ParserException { + final Pair result = myParser.parse(tokens, state); + return Pair.create(myFunction.fun(result.getFirst()), result.getSecond()); + } } } From a6609f04e346efa5e7c20b4dd8e94390c7154bc4 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 20:47:38 +0400 Subject: [PATCH 17/24] Fixed IAE at PyClassImpl.getMROAncestorTypes (EA-47519) --- .../python/psi/impl/PyClassImpl.java | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index b9fa2e3be6c1..0a7addcb4f9c 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -3,6 +3,7 @@ package com.jetbrains.python.psi.impl; import com.intellij.codeInsight.completion.CompletionUtil; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -15,7 +16,10 @@ import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.*; import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; -import com.jetbrains.python.*; +import com.jetbrains.python.PyElementTypes; +import com.jetbrains.python.PyNames; +import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.PythonDialectsTokenSetProvider; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; @@ -53,22 +57,16 @@ public class PyClassImpl extends PyPresentableElementImpl implement private volatile Map myPropertyCache; - private class CachedAncestorsProvider implements CachedValueProvider> { - @Nullable private TypeEvalContext myCachedContext; - + private class CachedAncestorsProvider implements ParameterizedCachedValueProvider, TypeEvalContext> { @Nullable @Override - public Result> compute() { - final TypeEvalContext context = myCachedContext != null ? myCachedContext : TypeEvalContext.codeInsightFallback(); + public CachedValueProvider.Result> compute(@NotNull TypeEvalContext context) { final List ancestorTypes = isNewStyleClass() ? getMROAncestorTypes(context) : getOldStyleAncestorTypes(context); - return Result.create(ancestorTypes, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); - } - - private void setTypeEvalContext(@Nullable TypeEvalContext cachedContext) { - myCachedContext = cachedContext; + return CachedValueProvider.Result.create(ancestorTypes, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); } } + private final Key, TypeEvalContext>> myCachedValueKey = Key.create("cached ancestors"); private final CachedAncestorsProvider myCachedAncestorsProvider = new CachedAncestorsProvider(); @Override @@ -1098,14 +1096,9 @@ public class PyClassImpl extends PyPresentableElementImpl implement @NotNull @Override public List getAncestorTypes(@NotNull TypeEvalContext context) { - myCachedAncestorsProvider.setTypeEvalContext(context); - try { - // TODO: Return different cached copies depending on the type eval context parameters - return CachedValuesManager.getManager(getProject()).getCachedValue(this, myCachedAncestorsProvider); - } - finally { - myCachedAncestorsProvider.setTypeEvalContext(null); - } + // TODO: Return different cached copies depending on the type eval context parameters + final CachedValuesManager manager = CachedValuesManager.getManager(getProject()); + return manager.getParameterizedCachedValue(this, myCachedValueKey, myCachedAncestorsProvider, false, context); } @NotNull From 8e9e4de7121597203b9a0d9a6a6759a8ff038e99 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 5 Aug 2013 21:08:59 +0400 Subject: [PATCH 18/24] Don't show parameters for multi-parameter types if all parameters are unknown --- .../jetbrains/python/documentation/PyTypeModelBuilder.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java index 9849f3843d53..8e42abebebe8 100644 --- a/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java +++ b/python/src/com/jetbrains/python/documentation/PyTypeModelBuilder.java @@ -165,7 +165,10 @@ public class PyTypeModelBuilder { final PyTupleType tupleType = (PyTupleType)elementType; final int n = tupleType.getElementCount(); for (int i = 0; i < n; i++) { - elementTypes.add(build(tupleType.getElementType(i), true)); + final PyType t = tupleType.getElementType(i); + if (t != null) { + elementTypes.add(build(t, true)); + } } } else if (elementType != null) { From 443bbd745ab8658f14ca53a656047834167cd0ba Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 6 Aug 2013 15:44:35 +0400 Subject: [PATCH 19/24] Fixed updating gutter icons for user skeletons (PY-10161) --- .../userSkeletons/PyUserSkeletonsLineMarkerProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsLineMarkerProvider.java b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsLineMarkerProvider.java index 624c67e7ce7a..1d538b219042 100644 --- a/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsLineMarkerProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/userSkeletons/PyUserSkeletonsLineMarkerProvider.java @@ -39,7 +39,7 @@ public class PyUserSkeletonsLineMarkerProvider implements LineMarkerProvider { final PyElement skeleton = getUserSkeleton(element); if (skeleton != null) { result.add(new LineMarkerInfo( - element, element.getTextRange(), ICON, Pass.VISIBLE_LINE_MARKERS, + element, element.getTextRange(), ICON, Pass.UPDATE_OVERRIDEN_MARKERS, new Function() { @Override public String fun(PsiElement e) { From 28455425d7f44e118ce1bce71505a4ff1a9066e0 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Aug 2013 11:15:22 +0200 Subject: [PATCH 20/24] EA-47930 - NPE: PyInstalledPackagesPanel.getSelectedSdk --- .../python/packaging/ui/PyInstalledPackagesPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index c4ab81b1eaa8..2f54b99a80b1 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -57,7 +57,8 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { } private Sdk getSelectedSdk() { - return ((PyPackageManagementService) myPackageManagementService).getSdk(); + PyPackageManagementService service = (PyPackageManagementService)myPackageManagementService; + return service != null ? service.getSdk() : null; } public void updateNotifications(@NotNull final Sdk selectedSdk) { From 8ec523af3feae63b083d983f3b1febf3d27ea0e3 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Aug 2013 13:12:12 +0200 Subject: [PATCH 21/24] don't lose location when resolving members of union type --- .../src/com/jetbrains/python/psi/types/PyUnionType.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/types/PyUnionType.java b/python/src/com/jetbrains/python/psi/types/PyUnionType.java index c69beca25529..ef6688bc5c61 100644 --- a/python/src/com/jetbrains/python/psi/types/PyUnionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyUnionType.java @@ -29,17 +29,17 @@ public class PyUnionType implements PyType { @NotNull AccessDirection direction, @NotNull PyResolveContext resolveContext) { SmartList ret = new SmartList(); - boolean all_nulls = true; + boolean allNulls = true; for (PyType member : myMembers) { if (member != null) { - List result = member.resolveMember(name, null, direction, resolveContext); + List result = member.resolveMember(name, location, direction, resolveContext); if (result != null) { - all_nulls = false; + allNulls = false; ret.addAll(result); } } } - return all_nulls ? null : ret; + return allNulls ? null : ret; } public Object[] getCompletionVariants(String completionPrefix, PyExpression location, ProcessingContext context) { From 1e2f44c25dfb499a682033420f26371a5e3ed5b0 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Aug 2013 13:15:04 +0200 Subject: [PATCH 22/24] allow resolving a PyDynamicMember to an assignment statement --- .../python/codeInsight/PyDynamicMember.java | 5 +++ .../python/codeInsight/PyPsiPath.java | 39 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/python/psi-api/src/com/jetbrains/python/codeInsight/PyDynamicMember.java b/python/psi-api/src/com/jetbrains/python/codeInsight/PyDynamicMember.java index 3e5e117a30eb..7505d3a0ca07 100644 --- a/python/psi-api/src/com/jetbrains/python/codeInsight/PyDynamicMember.java +++ b/python/psi-api/src/com/jetbrains/python/codeInsight/PyDynamicMember.java @@ -102,6 +102,11 @@ public class PyDynamicMember { return this; } + public PyDynamicMember toAssignment(String assignee) { + myPsiPath = new PyPsiPath.ToAssignment(myPsiPath, assignee); + return this; + } + public PyDynamicMember toPsiElement(final PsiElement psiElement) { myPsiPath = new PyPsiPath() { diff --git a/python/psi-api/src/com/jetbrains/python/codeInsight/PyPsiPath.java b/python/psi-api/src/com/jetbrains/python/codeInsight/PyPsiPath.java index c773926433a6..a9e96f7f8b3f 100644 --- a/python/psi-api/src/com/jetbrains/python/codeInsight/PyPsiPath.java +++ b/python/psi-api/src/com/jetbrains/python/codeInsight/PyPsiPath.java @@ -240,4 +240,43 @@ public abstract class PyPsiPath { } } } + + public static class ToAssignment extends PyPsiPath { + private final PyPsiPath myParent; + private final String myAssignee; + + public ToAssignment(PyPsiPath parent, String assignee) { + myParent = parent; + myAssignee = assignee; + } + + @Nullable + @Override + public PsiElement resolve(PsiElement context) { + PsiElement parent = myParent.resolve(context); + if (parent == null) { + return null; + } + AssignmentFinder finder = new AssignmentFinder(myAssignee); + parent.accept(finder); + return finder.myResult != null ? finder.myResult : parent; + } + } + + private static class AssignmentFinder extends PyRecursiveElementVisitor { + private final String myAssignee; + private PsiElement myResult; + + public AssignmentFinder(String assignee) { + myAssignee = assignee; + } + + @Override + public void visitPyAssignmentStatement(PyAssignmentStatement node) { + PyExpression lhs = node.getLeftHandSideExpression(); + if (lhs != null && myAssignee.equals(lhs.getText())) { + myResult = node; + } + } + } } From b7b65e3a64a35b8d78f8f8654522b3773fb17e7c Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Aug 2013 14:21:53 +0200 Subject: [PATCH 23/24] pass location to PyClassMembersProvider; complete request.user (PY-10452) --- .../python/psi/types/PyClassMembersProvider.java | 4 ++-- .../psi/types/PyClassMembersProviderBase.java | 6 +++--- .../stdlib/PyStdlibClassMembersProvider.java | 3 ++- .../PyUnresolvedReferencesInspection.java | 8 +++++--- .../python/psi/types/PyClassTypeImpl.java | 16 ++++++++-------- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProvider.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProvider.java index dd6b8c0eda59..672b37fffd0b 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProvider.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProvider.java @@ -13,8 +13,8 @@ import java.util.Collection; public interface PyClassMembersProvider { ExtensionPointName EP_NAME = ExtensionPointName.create("Pythonid.pyClassMembersProvider"); - Collection getMembers(PyClassType clazz); + Collection getMembers(PyClassType clazz, @Nullable PsiElement location); @Nullable - PsiElement resolveMember(PyClassType clazz, String name); + PsiElement resolveMember(PyClassType clazz, String name, @Nullable PsiElement location); } diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java index 4420221c9d6e..ef91d4ef8ddb 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassMembersProviderBase.java @@ -13,13 +13,13 @@ import java.util.Collections; */ public class PyClassMembersProviderBase implements PyClassMembersProvider { @Override - public Collection getMembers(PyClassType clazz) { + public Collection getMembers(PyClassType clazz, PsiElement location) { return Collections.emptyList(); } @Override - public PsiElement resolveMember(PyClassType clazz, String name) { - final Collection members = getMembers(clazz); + public PsiElement resolveMember(PyClassType clazz, String name, PsiElement location) { + final Collection members = getMembers(clazz, location); return resolveMemberByName(members, clazz, name); } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibClassMembersProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibClassMembersProvider.java index e2576e7c8a4f..231ea319bb1d 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibClassMembersProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibClassMembersProvider.java @@ -1,6 +1,7 @@ package com.jetbrains.python.codeInsight.stdlib; import com.intellij.openapi.util.Key; +import com.intellij.psi.PsiElement; import com.jetbrains.python.codeInsight.PyDynamicMember; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFile; @@ -21,7 +22,7 @@ public class PyStdlibClassMembersProvider extends PyClassMembersProviderBase { private Key> mySocketMembersKey = Key.create("socket.members"); @Override - public Collection getMembers(PyClassType classType) { + public Collection getMembers(PyClassType classType, PsiElement location) { PyClass clazz = classType.getPyClass(); final String qualifiedName = clazz.getQualifiedName(); if ("socket._socketobject".equals(qualifiedName)) { diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 71c0faac02a2..ac6a5cd69131 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -653,7 +653,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { if (isDecoratedAsDynamic(cls, true)) { return true; } - if (hasUnresolvedDynamicMember((PyClassType)qtype, refText)) return true; + if (hasUnresolvedDynamicMember((PyClassType)qtype, reference, refText)) return true; } if (qtype instanceof CythonBuiltinType || (qtype instanceof CythonType && reference instanceof PyOperatorReference)) { @@ -668,9 +668,11 @@ public class PyUnresolvedReferencesInspection extends PyInspection { return false; } - private static boolean hasUnresolvedDynamicMember(@NotNull final PyClassType qtype, @NotNull final String refText) { + private static boolean hasUnresolvedDynamicMember(@NotNull final PyClassType qtype, + PsiReference reference, + @NotNull final String refText) { for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { - final Collection resolveResult = provider.getMembers(qtype); + final Collection resolveResult = provider.getMembers(qtype, reference.getElement()); for (PyDynamicMember member : resolveResult) { if (member.getName().equals(refText)) return true; } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index de9cc6b39c68..046bb1fac020 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -135,7 +135,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { @NotNull PyResolveContext resolveContext, boolean inherited) { final TypeEvalContext context = resolveContext.getTypeEvalContext(); - PsiElement classMember = resolveByOverridingMembersProviders(this, name); //overriding members provers have priority to normal resolve + PsiElement classMember = resolveByOverridingMembersProviders(this, name, location); //overriding members provers have priority to normal resolve if (classMember != null) { return ResolveResultList.to(classMember); } @@ -211,7 +211,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } if (inherited) { - classMember = resolveByMembersProviders(this, name); //ask providers after real class introspection as providers have less priority + classMember = resolveByMembersProviders(this, name, location); //ask providers after real class introspection as providers have less priority } if (classMember != null) { @@ -223,7 +223,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { if (type instanceof PyClassType) { final PyClass pyClass = ((PyClassType)type).getPyClass(); if (pyClass != null) { - PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name); + PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location); if (superMember != null) { return ResolveResultList.to(superMember); @@ -296,9 +296,9 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } @Nullable - private static PsiElement resolveByMembersProviders(PyClassType aClass, String name) { + private static PsiElement resolveByMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location) { for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { - final PsiElement resolveResult = provider.resolveMember(aClass, name); + final PsiElement resolveResult = provider.resolveMember(aClass, name, location); if (resolveResult != null) return resolveResult; } @@ -306,10 +306,10 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { } @Nullable - private static PsiElement resolveByOverridingMembersProviders(PyClassType aClass, String name) { + private static PsiElement resolveByOverridingMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location) { for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { if (provider instanceof PyOverridingClassMembersProvider) { - final PsiElement resolveResult = provider.resolveMember(aClass, name); + final PsiElement resolveResult = provider.resolveMember(aClass, name, location); if (resolveResult != null) return resolveResult; } } @@ -361,7 +361,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { // from providers for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { - for (PyDynamicMember member : provider.getMembers(this)) { + for (PyDynamicMember member : provider.getMembers(this, location)) { final String name = member.getName(); if (!namesAlready.contains(name)) { LookupElementBuilder lookupElementBuilder = LookupElementBuilder.create(name).withIcon(member.getIcon()).withTypeText(getName()); From 5cefc841c927eb37c23650e35f2806357c2f8e68 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Tue, 6 Aug 2013 21:43:22 +0400 Subject: [PATCH 24/24] Extensible comment injector API --- .../python/intelliLang/PyLanguageInjectionSupport.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java index dd7d70f929b2..3fe4555051ed 100644 --- a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java +++ b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java @@ -1,6 +1,6 @@ package com.jetbrains.python.intelliLang; -import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiLanguageInjectionHost; import com.jetbrains.python.patterns.PythonPatterns; import com.jetbrains.python.psi.PyElement; import org.intellij.plugins.intelliLang.inject.AbstractLanguageInjectionSupport; @@ -26,7 +26,12 @@ public class PyLanguageInjectionSupport extends AbstractLanguageInjectionSupport } @Override - public boolean useDefaultInjector(PsiElement host) { + public boolean isApplicableTo(PsiLanguageInjectionHost host) { return host instanceof PyElement; } + + @Override + public boolean useDefaultInjector(PsiLanguageInjectionHost host) { + return true; + } }