mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 14:23:28 +07:00
Make lambdas and functions equally Callable (PY-958, part PY-201).
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
33
python/src/com/jetbrains/python/psi/Callable.java
Normal file
33
python/src/com/jetbrains/python/psi/Callable.java
Normal 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();
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -3,6 +3,5 @@ package com.jetbrains.python.psi;
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public interface PyLambdaExpression extends PyExpression {
|
||||
PyParameterList getParameterList();
|
||||
public interface PyLambdaExpression extends PyExpression, Callable {
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
2
python/testData/paramInfo/LambdaVariousArgs.py
Normal file
2
python/testData/paramInfo/LambdaVariousArgs.py
Normal 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)
|
||||
3
python/testData/paramInfo/ReassignedLambda.py
Normal file
3
python/testData/paramInfo/ReassignedLambda.py
Normal file
@@ -0,0 +1,3 @@
|
||||
z = lambda x,y: x+y
|
||||
y = z
|
||||
y(<arg1>1, <arg2>2)
|
||||
1
python/testData/paramInfo/SimpleLambda.py
Normal file
1
python/testData/paramInfo/SimpleLambda.py
Normal file
@@ -0,0 +1 @@
|
||||
(lambda x: x+1)(<arg1>1)
|
||||
@@ -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
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user