diff --git a/python/src/com/jetbrains/python/PyParameterInfoHandler.java b/python/src/com/jetbrains/python/PyParameterInfoHandler.java index 6b3a8b752222..1c3c4dcad328 100644 --- a/python/src/com/jetbrains/python/PyParameterInfoHandler.java +++ b/python/src/com/jetbrains/python/PyParameterInfoHandler.java @@ -12,10 +12,10 @@ import org.jetbrains.annotations.NotNull; import java.util.*; -import static com.jetbrains.python.psi.PyCallExpression.PyMarkedFunction; +import static com.jetbrains.python.psi.PyCallExpression.PyMarkedCallee; /** - * @author yole + * @author dcheryasov */ public class PyParameterInfoHandler implements ParameterInfoHandler { @@ -34,7 +34,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler n_param_list = new ArrayList(raw_params.length); final List hint_texts = new ArrayList(raw_params.length); @@ -109,7 +109,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler expressionList = PyUtil.flattenedParens(returnStatement.getExpression()); - if (expressionsSize < 0) { - expressionsSize = expressionList.size(); - } - if (expressionsSize != expressionList.size()) { - return -1; + final Callable callable = markedFunction.getCallable(); + if (callable instanceof PyFunction) { + PyStatementList statementList = ((PyFunction)callable).getStatementList(); + PyReturnStatement[] returnStatements = PyUtil.getAllChildrenOfType(statementList, PyReturnStatement.class); + int expressionsSize = -1; + for (PyReturnStatement returnStatement : returnStatements) { + if (returnStatement.getExpression() instanceof PyCallExpression) { + return -1; + } + List expressionList = PyUtil.flattenedParens(returnStatement.getExpression()); + if (expressionsSize < 0) { + expressionsSize = expressionList.size(); + } + if (expressionsSize != expressionList.size()) { + return -1; + } } + return expressionsSize; } - return expressionsSize; } return -1; } diff --git a/python/src/com/jetbrains/python/psi/Callable.java b/python/src/com/jetbrains/python/psi/Callable.java new file mode 100644 index 000000000000..1bda98c72b93 --- /dev/null +++ b/python/src/com/jetbrains/python/psi/Callable.java @@ -0,0 +1,33 @@ +package com.jetbrains.python.psi; + +import com.jetbrains.python.psi.types.PyType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Something that can be called, passed parameters to, and return something back. + *
+ * User: dcheryasov + * Date: May 15, 2010 3:22:30 PM + */ +public interface Callable extends PyElement { + + /** + * @return a list of parameters passed to this callable, possibly empty. + */ + @NotNull + PyParameterList getParameterList(); + + /** + * @return the type of returned value. + */ + @Nullable + PyType getReturnType(); + + /** + * @return a methods returns itself, non-method callables return null. + */ + @Nullable + PyFunction asMethod(); + +} diff --git a/python/src/com/jetbrains/python/psi/PyArgumentList.java b/python/src/com/jetbrains/python/psi/PyArgumentList.java index 3fb208743be3..710fb22e3035 100644 --- a/python/src/com/jetbrains/python/psi/PyArgumentList.java +++ b/python/src/com/jetbrains/python/psi/PyArgumentList.java @@ -112,7 +112,7 @@ public interface PyArgumentList extends PyElement { * @return result of a resolveCallee() against the function call to which the parameter list belongs. */ @Nullable - PyCallExpression.PyMarkedFunction getMarkedFunction(); + PyCallExpression.PyMarkedCallee getMarkedCallee(); PyArgumentList getArgumentList(); } diff --git a/python/src/com/jetbrains/python/psi/PyCallExpression.java b/python/src/com/jetbrains/python/psi/PyCallExpression.java index 218ec725bac4..b1668de299a4 100644 --- a/python/src/com/jetbrains/python/psi/PyCallExpression.java +++ b/python/src/com/jetbrains/python/psi/PyCallExpression.java @@ -36,7 +36,7 @@ public interface PyCallExpression extends PyExpression { * Return is null if callee cannot be resolved. */ @Nullable - PyMarkedFunction resolveCallee(); + PyMarkedCallee resolveCallee(); /** * Checks if the unqualified name of the callee matches the specified text. @@ -49,21 +49,35 @@ public interface PyCallExpression extends PyExpression { /** * Couples function with a flag describing the way it is called. */ - class PyMarkedFunction { - PyFunction myFunction; + class PyMarkedCallee { + Callable myCallable; EnumSet myFlags; int myImplicitOffset; boolean myImplicitlyResolved; - public PyMarkedFunction(@NotNull PyFunction function, EnumSet flags, int offset, boolean implicitlyResolved) { - myFunction = function; + /** + * Method-oriented constructor. + * @param function the method (or any other callable, but why bother then). + * @param flags result of decorators or wrapping. + * @param offset implicit argument offset; parameters up to this are implicitly filled in the call. + * @param implicitlyResolved value for {@link #isImplicitlyResolved()} + */ + public PyMarkedCallee(@NotNull Callable function, EnumSet flags, int offset, boolean implicitlyResolved) { + myCallable = function; myFlags = flags; myImplicitOffset = offset; myImplicitlyResolved = implicitlyResolved; } - public PyFunction getFunction() { - return myFunction; + public PyMarkedCallee(Callable callable, boolean implicitlyResolved) { + myCallable = callable; + myFlags = EnumSet.noneOf(PyFunction.Flag.class); + myImplicitOffset = 0; + myImplicitlyResolved = implicitlyResolved; + } + + public Callable getCallable() { + return myCallable; } public EnumSet getFlags() { @@ -79,6 +93,9 @@ public interface PyCallExpression extends PyExpression { return myImplicitOffset; } + /** + * @return true iff the result is resolved based on divination and name similarity rather than by proper resolution process. + */ public boolean isImplicitlyResolved() { return myImplicitlyResolved; } diff --git a/python/src/com/jetbrains/python/psi/PyFunction.java b/python/src/com/jetbrains/python/psi/PyFunction.java index 1d3e3f8ec442..5e27e7bdf076 100644 --- a/python/src/com/jetbrains/python/psi/PyFunction.java +++ b/python/src/com/jetbrains/python/psi/PyFunction.java @@ -15,8 +15,11 @@ import org.jetbrains.annotations.Nullable; * * @author yole */ -public interface PyFunction extends PsiNamedElement, PsiNameIdentifierOwner, PyStatement, NameDefiner, PyDocStringOwner, StubBasedPsiElement, - ScopeOwner { +public interface PyFunction +extends + PsiNamedElement, StubBasedPsiElement, + PsiNameIdentifierOwner, PyStatement, Callable, NameDefiner, PyDocStringOwner, ScopeOwner +{ PyFunction[] EMPTY_ARRAY = new PyFunction[0]; /** @@ -28,9 +31,6 @@ public interface PyFunction extends PsiNamedElement, PsiNameIdentifierOwner, PyS @Nullable ASTNode getNameNode(); - @NotNull - PyParameterList getParameterList(); - PyStatementList getStatementList(); @Nullable @@ -46,9 +46,6 @@ public interface PyFunction extends PsiNamedElement, PsiNameIdentifierOwner, PyS */ boolean isTopLevel(); - @Nullable - PyType getReturnType(); - @Nullable PyType getReturnTypeFromDocString(); @@ -57,11 +54,11 @@ public interface PyFunction extends PsiNamedElement, PsiNameIdentifierOwner, PyS */ enum Flag { /** - * Function is decorated with @classmethod, first param is the class. + * Function is decorated with @classmethod, its first param is the class. */ CLASSMETHOD, /** - * Function is decorated with {@code @staticmethod}, first param is as in a regular function. + * Function is decorated with {@code @staticmethod}, its first param is as in a regular function. */ STATICMETHOD, diff --git a/python/src/com/jetbrains/python/psi/PyLambdaExpression.java b/python/src/com/jetbrains/python/psi/PyLambdaExpression.java index dadaa7d9952f..08253138f858 100644 --- a/python/src/com/jetbrains/python/psi/PyLambdaExpression.java +++ b/python/src/com/jetbrains/python/psi/PyLambdaExpression.java @@ -3,6 +3,5 @@ package com.jetbrains.python.psi; /** * @author yole */ -public interface PyLambdaExpression extends PyExpression { - PyParameterList getParameterList(); +public interface PyLambdaExpression extends PyExpression, Callable { } diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 66a9df467904..b062f27c0b9c 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -712,7 +712,7 @@ public class PyUtil { } PsiElement parent = elt.getParent(); while(parent != null) { - if (parent instanceof PyClass || parent instanceof PyFunction) { + if (parent instanceof PyClass || parent instanceof Callable) { return parent; } if (parent instanceof PsiFile) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java b/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java index 421c1e08c883..395041341c98 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyArgumentListImpl.java @@ -235,7 +235,7 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList // following the spec: http://docs.python.org/ref/calls.html PyCallExpression call = getCallExpression(); if (call != null) { - PyCallExpression.PyMarkedFunction resolved_callee = call.resolveCallee(); + PyCallExpression.PyMarkedCallee resolved_callee = call.resolveCallee(); ret.my_marked_func = resolved_callee; if (resolved_callee != null) { analyzeCall(arguments, resolved_callee, ret); @@ -244,9 +244,9 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList return ret; } - private static void analyzeCall(PyExpression[] arguments, PyCallExpression.PyMarkedFunction resolved_callee, AnalysisResultImpl ret) { - PyFunction func = resolved_callee.getFunction(); - PyParameterList paramlist = func.getParameterList(); + private static void analyzeCall(PyExpression[] arguments, PyCallExpression.PyMarkedCallee resolved_callee, AnalysisResultImpl ret) { + Callable callable = resolved_callee.getCallable(); + PyParameterList paramlist = callable.getParameterList(); PyParameter[] params = paramlist.getParameters(); // prepare args and slots List unmatched_args = new LinkedList(); @@ -616,7 +616,7 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList private final List my_kwd_mapped_params; // params mapped to **arg private final List my_unmapped_params; private final Map> my_arg_flags; // flags of every arg - private PyCallExpression.PyMarkedFunction my_marked_func; + private PyCallExpression.PyMarkedCallee my_marked_func; public AnalysisResultImpl() { // full of empty containers @@ -685,7 +685,7 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList * @return result of a resolveCallee() against the function call to which the paramater list belongs. */ @Nullable - public PyCallExpression.PyMarkedFunction getMarkedFunction() { + public PyCallExpression.PyMarkedCallee getMarkedCallee() { return my_marked_func; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index 2b31c7b29c7e..74242a6d1c0e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -5,7 +5,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNamedElement; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.resolve.ImplicitResolveResult; import com.jetbrains.python.psi.resolve.QualifiedResolveResult; import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyType; @@ -72,50 +71,56 @@ public class PyCallExpressionHelper { } @Nullable - public static PyCallExpression.PyMarkedFunction resolveCallee(PyCallExpression us) { + public static PyCallExpression.PyMarkedCallee resolveCallee(PyCallExpression us) { PyExpression callee = us.getCallee(); PyFunction.Flag wrapped_flag = null; boolean is_constructor_call = false; + PsiElement resolved; + QualifiedResolveResult resolveResult = null; if (callee instanceof PyReferenceExpression) { + // dereference PyReferenceExpression ref = (PyReferenceExpression)callee; - QualifiedResolveResult resolveResult = ref.followAssignmentsChain(); - PsiElement resolved = resolveResult.getElement(); - if (resolved instanceof PyClass) { - resolved = ((PyClass)resolved).findInitOrNew(true); // class to constructor call - is_constructor_call = true; + resolveResult = ref.followAssignmentsChain(); + resolved = resolveResult.getElement(); + } + else resolved = callee; + // analyze + if (resolved instanceof PyClass) { + resolved = ((PyClass)resolved).findInitOrNew(true); // class to constructor call + is_constructor_call = true; + } + else if (resolved instanceof PyCallExpression) { + // is it a case of "foo = classmethod(foo)"? + PyCallExpression redefining_call = (PyCallExpression)resolved; + Pair wrapper_info = interpretAsStaticmethodOrClassmethodWrappingCall(redefining_call, us); + if (wrapper_info != null) { + resolved = wrapper_info.getSecond(); + String wrapper_name = wrapper_info.getFirst(); + if (PyNames.CLASSMETHOD.equals(wrapper_name)) wrapped_flag = PyFunction.Flag.CLASSMETHOD; + else if (PyNames.STATICMETHOD.equals(wrapper_name)) wrapped_flag = PyFunction.Flag.STATICMETHOD; } - else if (resolved instanceof PyCallExpression) { - // is it a case of "foo = classmethod(foo)"? - PyCallExpression redefining_call = (PyCallExpression)resolved; - Pair wrapper_info = interpretAsStaticmethodOrClassmethodWrappingCall(redefining_call, us); - if (wrapper_info != null) { - resolved = wrapper_info.getSecond(); - String wrapper_name = wrapper_info.getFirst(); - if (PyNames.CLASSMETHOD.equals(wrapper_name)) wrapped_flag = PyFunction.Flag.CLASSMETHOD; - else if (PyNames.STATICMETHOD.equals(wrapper_name)) wrapped_flag = PyFunction.Flag.STATICMETHOD; - } + } + if (resolved instanceof Callable) { + EnumSet flags = EnumSet.noneOf(PyFunction.Flag.class); + PyExpression last_qualifier = resolveResult != null? resolveResult.getLastQualifier() : null; + final PyExpression call_reference = us.getCallee(); + boolean is_by_instance = isByInstance(call_reference); + if (last_qualifier != null) { + PyType qualifier_type = last_qualifier.getType(TypeEvalContext.fast()); // NOTE: ...or slow()? + is_by_instance |= (qualifier_type != null && qualifier_type instanceof PyClassType && !((PyClassType)qualifier_type).isDefinition()); } - if (resolved instanceof PyFunction) { - EnumSet flags = EnumSet.noneOf(PyFunction.Flag.class); - PyExpression last_qualifier = resolveResult.getLastQualifier(); - final PyExpression call_reference = us.getCallee(); - boolean is_by_instance = isByInstance(call_reference); - if (last_qualifier != null) { - PyType qualifier_type = last_qualifier.getType(TypeEvalContext.fast()); // NOTE: ...or slow()? - is_by_instance |= (qualifier_type != null && qualifier_type instanceof PyClassType && !((PyClassType)qualifier_type).isDefinition()); - } - int implicit_offset = getImplicitArgumentCount(call_reference, (PyFunction) resolved, wrapped_flag, flags, is_by_instance); - if (! is_constructor_call && PyNames.NEW.equals(((PyFunction)resolved).getName())) { - implicit_offset = Math.min(implicit_offset-1, 0); // case of Class.__new__ - } - return new PyCallExpression.PyMarkedFunction((PyFunction)resolved, flags, implicit_offset, resolveResult.isImplicit()); + final Callable callable = (Callable)resolved; + int implicit_offset = getImplicitArgumentCount(call_reference, callable, wrapped_flag, flags, is_by_instance); + if (! is_constructor_call && PyNames.NEW.equals(callable.getName())) { + implicit_offset = Math.min(implicit_offset-1, 0); // case of Class.__new__ } + return new PyCallExpression.PyMarkedCallee(callable, flags, implicit_offset, resolveResult != null? resolveResult.isImplicit() : false); } return null; } /** - * Calls the {@link #getImplicitArgumentCount(PyExpression, PyFunction, PyFunction.Flag, EnumSet, boolean) full version} + * Calls the {@link #getImplicitArgumentCount(PyExpression, Callable, PyFunction.Flag, EnumSet, boolean) full version} * with null flags and with isByInstance inferred directly from call site (won't work with reassigned bound methods). * @param callReference the call site, where arguments are given. * @param functionBeingCalled resolved method which is being called; plain functions are OK but make little sense. @@ -128,7 +133,7 @@ public class PyCallExpressionHelper { /** * Finds how many arguments are implicit in a given call. * @param callReference the call site, where arguments are given. - * @param method resolved method which is being called; plain functions are OK but make little sense. + * @param callable resolved method which is being called; other callables are OK but immediately return 0. * @param wrappedFlag value of {@link PyFunction.Flag#WRAPPED} if known. * @param flags set of flags to be updated by this call; wrappedFlag's value ends up here, too. * @param isByInstance true if the call is known to be by instance (not by class). @@ -136,12 +141,15 @@ public class PyCallExpressionHelper { * because one parameter ('self') is implicit. */ private static int getImplicitArgumentCount(final PyExpression callReference, - PyFunction method, + Callable callable, @Nullable PyFunction.Flag wrappedFlag, @Nullable EnumSet flags, boolean isByInstance ) { int implicit_offset = 0; + PyFunction method = callable.asMethod(); + if (method == null) return implicit_offset; + // if (isByInstance) implicit_offset += 1; // wrapped flags? if (wrappedFlag != null) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java index 698e6e50e8a0..8f6496564aab 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java @@ -30,8 +30,10 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress @Nullable public PyExpression getCallee() { - //return PsiTreeUtil.getChildOfType(this, PyReferenceExpression.class); what we call can be whatever expr, not always a ref - return (PyExpression)getFirstChild(); + // peel off any parens, because we may have smth like (lambda x: x+1)(2) + PyExpression seeker = (PyExpression)getFirstChild(); + while (seeker instanceof PyParenthesizedExpression) seeker = ((PyParenthesizedExpression)seeker).getContainedExpression(); + return seeker; } public PyArgumentList getArgumentList() { @@ -48,7 +50,7 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress PyCallExpressionHelper.addArgument(this, expression); } - public PyMarkedFunction resolveCallee() { + public PyMarkedCallee resolveCallee() { return PyCallExpressionHelper.resolveCallee(this); } @@ -94,16 +96,18 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress if (providedType != null) { return providedType; } - if (target instanceof PyFunction) { - final PyFunction function = (PyFunction)target; + if (target instanceof Callable) { + final Callable callable = (Callable)target; if (context.allowReturnTypes()) { - return function.getReturnType(); + return callable.getReturnType(); } - final PyType docStringType = function.getReturnTypeFromDocString(); - if (docStringType != null) { - return docStringType; + if (callable instanceof PyFunction) { + final PyType docStringType = ((PyFunction)callable).getReturnTypeFromDocString(); + if (docStringType != null) { + return docStringType; + } } - return new PyReturnTypeReference((PyFunction)target); + return new PyReturnTypeReference(callable); } } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java index 16cc9cdfd529..a50f004f89af 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java @@ -94,11 +94,12 @@ public class PyDecoratorImpl extends PyPresentableElementImpl i PyCallExpressionHelper.addArgument(this, expression); } - public PyMarkedFunction resolveCallee() { - PyMarkedFunction callee = PyCallExpressionHelper.resolveCallee(this); + public PyMarkedCallee resolveCallee() { + PyMarkedCallee callee = PyCallExpressionHelper.resolveCallee(this); if (callee == null) return null; if (! hasArgumentList()) { - callee = new PyMarkedFunction(callee.getFunction(), callee.getFlags(), callee.getImplicitOffset() + 1, callee.isImplicitlyResolved()); + // NOTE: that +1 thing looks fishy + callee = new PyMarkedCallee(callee.getCallable(), callee.getFlags(), callee.getImplicitOffset() + 1, callee.isImplicitlyResolved()); } return callee; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 7ff4b83d8568..570808635b30 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -127,6 +127,11 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp return visitor.result(); } + public PyFunction asMethod() { + if (getContainingClass() != null) return this; + else return null; + } + public PyType getReturnTypeFromDocString() { String typeName; final PyFunctionStub stub = getStub(); diff --git a/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java index 8d10d0ae59cb..2e998893e9a9 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyLambdaExpressionImpl.java @@ -4,6 +4,7 @@ import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.ResolveState; import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.PyType; @@ -26,10 +27,21 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp return null; } + @NotNull public PyParameterList getParameterList() { return childToPsiNotNull(PyElementTypes.PARAMETER_LIST_SET, 0); } + public PyType getReturnType() { + PyExpression body = PsiTreeUtil.getChildOfType(this, PyExpression.class); + if (body != null) return body.getType(TypeEvalContext.fast()); // or slow? + else return null; + } + + public PyFunction asMethod() { + return null; // we're never a method + } + public boolean processDeclarations(@NotNull final PsiScopeProcessor processor, @NotNull final ResolveState state, final PsiElement lastParent, diff --git a/python/src/com/jetbrains/python/psi/types/PyReturnTypeReference.java b/python/src/com/jetbrains/python/psi/types/PyReturnTypeReference.java index 7c75c0a8d814..31293a0ae98e 100644 --- a/python/src/com/jetbrains/python/psi/types/PyReturnTypeReference.java +++ b/python/src/com/jetbrains/python/psi/types/PyReturnTypeReference.java @@ -1,26 +1,26 @@ package com.jetbrains.python.psi.types; import com.intellij.psi.PsiElement; +import com.jetbrains.python.psi.Callable; import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.resolve.PyResolveContext; import org.jetbrains.annotations.Nullable; /** * @author yole */ public class PyReturnTypeReference extends PyTypeReferenceImpl { - private final PyFunction myFunction; + private final Callable myCallable; - public PyReturnTypeReference(PyFunction function) { - myFunction = function; + public PyReturnTypeReference(Callable callable) { + myCallable = callable; } @Nullable public PyType resolve(PsiElement context) { - return myFunction.getReturnType(); + return myCallable.getReturnType(); } public String getName() { - return "return type of " + myFunction.getPresentation().getPresentableText(); + return "return type of " + myCallable.getPresentation().getPresentableText(); } } diff --git a/python/src/com/jetbrains/python/refactoring/classes/PyDependentMembersCollector.java b/python/src/com/jetbrains/python/refactoring/classes/PyDependentMembersCollector.java index 8abc72c6725f..240d71e2114a 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/PyDependentMembersCollector.java +++ b/python/src/com/jetbrains/python/refactoring/classes/PyDependentMembersCollector.java @@ -16,8 +16,8 @@ public class PyDependentMembersCollector extends DependentMembersCollectorBase

1, 2, 4, k=5) diff --git a/python/testData/paramInfo/ReassignedLambda.py b/python/testData/paramInfo/ReassignedLambda.py new file mode 100644 index 000000000000..eb3c3f0a4eee --- /dev/null +++ b/python/testData/paramInfo/ReassignedLambda.py @@ -0,0 +1,3 @@ +z = lambda x,y: x+y +y = z +y(1, 2) diff --git a/python/testData/paramInfo/SimpleLambda.py b/python/testData/paramInfo/SimpleLambda.py new file mode 100644 index 000000000000..cf9e06a579fa --- /dev/null +++ b/python/testData/paramInfo/SimpleLambda.py @@ -0,0 +1 @@ +(lambda x: x+1)(1) diff --git a/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java b/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java index 9badb041754b..e48073262c0a 100644 --- a/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java +++ b/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java @@ -296,6 +296,30 @@ public class PyParameterInfoTest extends LightMarkedTestCase { feignCtrlP(marks.get("").getTextOffset()).check("a,b", new String[]{"b"}); } + public void testSimpleLambda() throws Exception { + Map marks = loadTest(); + assertEquals("Test data sanity", marks.size(), 1); + + feignCtrlP(marks.get("").getTextOffset()).check("x", new String[]{"x"}); + } + + public void testReassignedLambda() throws Exception { + Map marks = loadTest(); + assertEquals("Test data sanity", marks.size(), 2); + + feignCtrlP(marks.get("").getTextOffset()).check("x,y", new String[]{"x,"}); + feignCtrlP(marks.get("").getTextOffset()).check("x,y", new String[]{"y"}); + } + + public void testLambdaVariousArgs() throws Exception { + Map marks = loadTest(); + assertEquals("Test data sanity", marks.size(), 4); + + feignCtrlP(marks.get("").getTextOffset()).check("x,y=1,*args,**kwargs", new String[]{"x,"}); + feignCtrlP(marks.get("").getTextOffset()).check("x,y=1,*args,**kwargs", new String[]{"y=1,"}); + feignCtrlP(marks.get("").getTextOffset()).check("x,y=1,*args,**kwargs", new String[]{"*args,"}); + feignCtrlP(marks.get("").getTextOffset()).check("x,y=1,*args,**kwargs", new String[]{"**kwargs"}); + } // TODO: add method tests with decorators when a mock SDK is available diff --git a/python/testSrc/com/jetbrains/python/PyResolveCalleeTest.java b/python/testSrc/com/jetbrains/python/PyResolveCalleeTest.java index 22e581b682ac..1e6172e9c3bf 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveCalleeTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveCalleeTest.java @@ -15,33 +15,33 @@ import java.util.EnumSet; */ public class PyResolveCalleeTest extends PyResolveTestCase { - private PyCallExpression.PyMarkedFunction resolveCallee() throws Exception { + private PyCallExpression.PyMarkedCallee resolveCallee() throws Exception { PsiReference ref = configureByFile(getTestName(false) + ".py"); PyCallExpression call = PsiTreeUtil.getParentOfType(ref.getElement(), PyCallExpression.class); return call.resolveCallee(); } public void testInstanceCall() throws Exception { - PyCallExpression.PyMarkedFunction resolved = resolveCallee(); - assertNotNull(resolved.getFunction()); + PyCallExpression.PyMarkedCallee resolved = resolveCallee(); + assertNotNull(resolved.getCallable()); assertEquals(1, resolved.getImplicitOffset()); } public void testClassCall() throws Exception { - PyCallExpression.PyMarkedFunction resolved = resolveCallee(); - assertNotNull(resolved.getFunction()); + PyCallExpression.PyMarkedCallee resolved = resolveCallee(); + assertNotNull(resolved.getCallable()); assertTrue(resolved.getFlags().equals(EnumSet.noneOf(PyFunction.Flag.class))); } public void testDecoCall() throws Exception { - PyCallExpression.PyMarkedFunction resolved = resolveCallee(); - assertNotNull(resolved.getFunction()); + PyCallExpression.PyMarkedCallee resolved = resolveCallee(); + assertNotNull(resolved.getCallable()); assertEquals(1, resolved.getImplicitOffset()); } public void testDecoParamCall() throws Exception { - PyCallExpression.PyMarkedFunction resolved = resolveCallee(); - assertNotNull(resolved.getFunction()); + PyCallExpression.PyMarkedCallee resolved = resolveCallee(); + assertNotNull(resolved.getCallable()); assertTrue(resolved.getFlags().equals(EnumSet.noneOf(PyFunction.Flag.class))); } diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index f881f51d6673..47a719197b3b 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -140,6 +140,11 @@ public class PyResolveTest extends PyResolveTestCase { assertTrue(targetElement instanceof PyNamedParameter); } + public void testLambdaParameterOutside() { + PsiElement targetElement = resolve(); + assertTrue(targetElement instanceof PyTargetExpression); + } + public void testSuperField() { PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression);