Make lambdas and functions equally Callable (PY-958, part PY-201).

This commit is contained in:
Dmitry Cheryasov
2010-05-16 18:03:07 +03:00
parent 572c6a97ff
commit e73e9cf966
24 changed files with 234 additions and 121 deletions

View File

@@ -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<PyArgumentList, PyArgumentList.AnalysisResult> {
@@ -34,7 +34,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
PyArgumentList arglist = findArgumentList(context);
if (arglist != null) {
PyArgumentList.AnalysisResult result = arglist.analyzeCall();
if (result != null && result.getMarkedFunction() != null) {
if (result != null && result.getMarkedCallee() != null) {
context.setItemsToShow(new Object[] { result });
return arglist;
}
@@ -93,12 +93,12 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
public void updateUI(final PyArgumentList.AnalysisResult result, final ParameterInfoUIContext context) {
if (result == null) return;
PyMarkedFunction marked = result.getMarkedFunction();
PyMarkedCallee marked = result.getMarkedCallee();
assert marked != null : "findElementForParameterInfo() did it wrong!";
final PyFunction py_function = marked.getFunction();
if (py_function == null) return; // resolution failed
final Callable callable = marked.getCallable();
if (callable == null) return; // resolution failed
PyParameter[] raw_params = py_function.getParameterList().getParameters();
PyParameter[] raw_params = callable.getParameterList().getParameters();
final List<PyNamedParameter> n_param_list = new ArrayList<PyNamedParameter>(raw_params.length);
final List<String> hint_texts = new ArrayList<String>(raw_params.length);
@@ -109,7 +109,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
// build the textual picture and the list of named parameters
ParamHelper.walkDownParamArray(
py_function.getParameterList().getParameters(),
callable.getParameterList().getParameters(),
new ParamHelper.ParamWalker() {
public void enterTupleParameter(PyTupleParameter param, boolean first, boolean last) {
hint_flags.put(hint_texts.size(), EnumSet.noneOf(ParameterInfoUIContextEx.Flag.class));

View File

@@ -3,7 +3,6 @@ package com.jetbrains.python.inspections;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor;
import com.jetbrains.python.PyBundle;
@@ -72,15 +71,15 @@ public class PyArgumentListInspection extends LocalInspectionTool {
for (PyDecorator deco : decos) {
if (! deco.hasArgumentList()) {
// empty arglist; deco function must have a non-kwarg first arg
PyCallExpression.PyMarkedFunction mkfunc = deco.resolveCallee();
PyCallExpression.PyMarkedCallee mkfunc = deco.resolveCallee();
if (mkfunc != null) {
PyFunction decofunc = mkfunc.getFunction();
Callable callable = mkfunc.getCallable();
int first_param_offset = mkfunc.getImplicitOffset();
PyParameter[] params = decofunc.getParameterList().getParameters();
PyParameter[] params = callable.getParameterList().getParameters();
PyNamedParameter alleged_first_param = params.length < first_param_offset ? null : params[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", decofunc.getName()));
registerProblem(deco, PyBundle.message("INSP.func.$0.lacks.first.arg", callable.getName())); // TODO: better names for anon lambdas
}
else {
// possible unfilled params

View File

@@ -116,24 +116,27 @@ public class PyStringFormatInspection extends LocalInspectionTool {
return inspectArguments((PyExpression)element);
}
else if (rightExpression instanceof PyCallExpression) {
final PyCallExpression.PyMarkedFunction markedFunction = ((PyCallExpression)rightExpression).resolveCallee();
final PyCallExpression.PyMarkedCallee markedFunction = ((PyCallExpression)rightExpression).resolveCallee();
if (markedFunction != null) {
PyStatementList statementList = markedFunction.getFunction().getStatementList();
PyReturnStatement[] returnStatements = PyUtil.getAllChildrenOfType(statementList, PyReturnStatement.class);
int expressionsSize = -1;
for (PyReturnStatement returnStatement : returnStatements) {
if (returnStatement.getExpression() instanceof PyCallExpression) {
return -1;
}
List<PyExpression> 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<PyExpression> expressionList = PyUtil.flattenedParens(returnStatement.getExpression());
if (expressionsSize < 0) {
expressionsSize = expressionList.size();
}
if (expressionsSize != expressionList.size()) {
return -1;
}
}
return expressionsSize;
}
return expressionsSize;
}
return -1;
}

View File

@@ -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.
* <br/>
* 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();
}

View File

@@ -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();
}

View File

@@ -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<PyFunction.Flag> myFlags;
int myImplicitOffset;
boolean myImplicitlyResolved;
public PyMarkedFunction(@NotNull PyFunction function, EnumSet<PyFunction.Flag> 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<PyFunction.Flag> 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<PyFunction.Flag> 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;
}

View File

@@ -15,8 +15,11 @@ import org.jetbrains.annotations.Nullable;
*
* @author yole
*/
public interface PyFunction extends PsiNamedElement, PsiNameIdentifierOwner, PyStatement, NameDefiner, PyDocStringOwner, StubBasedPsiElement<PyFunctionStub>,
ScopeOwner {
public interface PyFunction
extends
PsiNamedElement, StubBasedPsiElement<PyFunctionStub>,
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,

View File

@@ -3,6 +3,5 @@ package com.jetbrains.python.psi;
/**
* @author yole
*/
public interface PyLambdaExpression extends PyExpression {
PyParameterList getParameterList();
public interface PyLambdaExpression extends PyExpression, Callable {
}

View File

@@ -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) {

View File

@@ -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<PyExpression> unmatched_args = new LinkedList<PyExpression>();
@@ -616,7 +616,7 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
private final List<PyNamedParameter> my_kwd_mapped_params; // params mapped to **arg
private final List<PyNamedParameter> my_unmapped_params;
private final Map<PyExpression, EnumSet<PyArgumentList.ArgFlag>> 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;
}

View File

@@ -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<String, PyFunction> 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<String, PyFunction> 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<PyFunction.Flag> 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<PyFunction.Flag> 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<PyFunction.Flag>, boolean) full version}
* Calls the {@link #getImplicitArgumentCount(PyExpression, Callable, PyFunction.Flag, EnumSet<PyFunction.Flag>, 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 <i>updated</i> 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<PyFunction.Flag> 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) {

View File

@@ -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);
}
}
}

View File

@@ -94,11 +94,12 @@ public class PyDecoratorImpl extends PyPresentableElementImpl<PyDecoratorStub> 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;
}

View File

@@ -127,6 +127,11 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
return visitor.result();
}
public PyFunction asMethod() {
if (getContainingClass() != null) return this;
else return null;
}
public PyType getReturnTypeFromDocString() {
String typeName;
final PyFunctionStub stub = getStub();

View File

@@ -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,

View File

@@ -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();
}
}

View File

@@ -16,8 +16,8 @@ public class PyDependentMembersCollector extends DependentMembersCollectorBase<P
final PyRecursiveElementVisitor visitor = new PyRecursiveElementVisitor() {
@Override
public void visitPyCallExpression(PyCallExpression node) {
final PyCallExpression.PyMarkedFunction markedFunction = node.resolveCallee();
final PyFunction function = markedFunction != null ? markedFunction.getFunction() : null;
final PyCallExpression.PyMarkedCallee markedFunction = node.resolveCallee();
final PyFunction function = markedFunction != null ? markedFunction.getCallable().asMethod() : null;
if (!existsInSuperClass(function)) {
myCollection.add(function);
}

View File

@@ -86,8 +86,8 @@ public class PyPushDownConflicts {
@Override
public void visitPyCallExpression(PyCallExpression node) {
final PyCallExpression.PyMarkedFunction markedFunction = node.resolveCallee();
final PyFunction function = markedFunction != null ? markedFunction.getFunction() : null;
final PyCallExpression.PyMarkedCallee markedFunction = node.resolveCallee();
final Callable function = markedFunction != null ? markedFunction.getCallable() : null;
if (myMovedMembers.contains(function)) {
myCollection.add(function);
}

View File

@@ -0,0 +1,2 @@
z = lambda x, y=1, *args, **kwargs: x * y + sum(args) * kwargs.get("k", 1)
z(<arg1>1, <arg2>2, <arg3>4, <arg4>k=5)

View File

@@ -0,0 +1,3 @@
z = lambda x,y: x+y
y = z
y(<arg1>1, <arg2>2)

View File

@@ -0,0 +1 @@
(lambda x: x+1)(<arg1>1)

View File

@@ -296,6 +296,30 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a,b", new String[]{"b"});
}
public void testSimpleLambda() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals("Test data sanity", marks.size(), 1);
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("x", new String[]{"x"});
}
public void testReassignedLambda() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals("Test data sanity", marks.size(), 2);
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("x,y", new String[]{"x,"});
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("x,y", new String[]{"y"});
}
public void testLambdaVariousArgs() throws Exception {
Map<String, PsiElement> marks = loadTest();
assertEquals("Test data sanity", marks.size(), 4);
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("x,y=1,*args,**kwargs", new String[]{"x,"});
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("x,y=1,*args,**kwargs", new String[]{"y=1,"});
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("x,y=1,*args,**kwargs", new String[]{"*args,"});
feignCtrlP(marks.get("<arg4>").getTextOffset()).check("x,y=1,*args,**kwargs", new String[]{"**kwargs"});
}
// TODO: add method tests with decorators when a mock SDK is available

View File

@@ -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)));
}

View File

@@ -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);