diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyCallExpression.java b/python/psi-api/src/com/jetbrains/python/psi/PyCallExpression.java
index 8b78522df015..bf42d69d6016 100644
--- a/python/psi-api/src/com/jetbrains/python/psi/PyCallExpression.java
+++ b/python/psi-api/src/com/jetbrains/python/psi/PyCallExpression.java
@@ -25,13 +25,12 @@ import com.jetbrains.python.nameResolver.NameResolverTools;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.types.PyCallableParameter;
+import com.jetbrains.python.psi.types.PyCallableType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
+import java.util.stream.Collectors;
/**
* Represents an entire call expression, like foo() or foo.bar[1]('x').
@@ -195,7 +194,11 @@ public interface PyCallExpression extends PyCallSiteExpression {
*/
@NotNull
default List multiResolveCalleeFunction(@NotNull PyResolveContext resolveContext) {
- return ContainerUtil.map(multiResolveRatedCallee(resolveContext, 0), PyRatedMarkedCallee::getElement);
+ return multiResolveRatedCallee(resolveContext, 0)
+ .stream()
+ .map(PyRatedMarkedCallee::getElement)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
}
/**
@@ -233,7 +236,9 @@ public interface PyCallExpression extends PyCallSiteExpression {
@NotNull
default List multiResolveRatedCalleeFunction(@NotNull PyResolveContext resolveContext) {
return ContainerUtil.map(multiResolveRatedCallee(resolveContext, 0),
- markedCallee -> new PyRatedCallee(markedCallee.getElement(), markedCallee.getRate()));
+ markedCallee -> new PyRatedCallee(markedCallee.getMarkedCallee().getCallableType(),
+ markedCallee.getElement(),
+ markedCallee.getRate()));
}
/**
@@ -431,7 +436,8 @@ public interface PyCallExpression extends PyCallSiteExpression {
* Couples function with a flag describing the way it is called.
*/
class PyMarkedCallee {
- @NotNull private final PyCallable myCallable;
+ @NotNull private final PyCallableType myCallableType;
+ @Nullable private final PyCallable myCallable;
@Nullable private final PyFunction.Modifier myModifier;
private final int myImplicitOffset;
private final boolean myImplicitlyResolved;
@@ -439,12 +445,18 @@ public interface PyCallExpression extends PyCallSiteExpression {
/**
* Method-oriented constructor.
*
+ * @param callableType type describing callable object
* @param function the method (or any other callable, but why bother then).
* @param modifier classmethod or staticmethod modifier
* @param offset implicit argument offset; parameters up to this are implicitly filled in the call.
* @param implicitlyResolved value for {@link #isImplicitlyResolved()}
*/
- public PyMarkedCallee(@NotNull PyCallable function, @Nullable PyFunction.Modifier modifier, int offset, boolean implicitlyResolved) {
+ public PyMarkedCallee(@NotNull PyCallableType callableType,
+ @Nullable PyCallable function,
+ @Nullable PyFunction.Modifier modifier,
+ int offset,
+ boolean implicitlyResolved) {
+ myCallableType = callableType;
myCallable = function;
myModifier = modifier;
myImplicitOffset = offset;
@@ -452,6 +464,11 @@ public interface PyCallExpression extends PyCallSiteExpression {
}
@NotNull
+ public PyCallableType getCallableType() {
+ return myCallableType;
+ }
+
+ @Nullable
public PyCallable getCallable() {
return myCallable;
}
@@ -480,12 +497,21 @@ public interface PyCallExpression extends PyCallSiteExpression {
class PyRatedCallee extends RatedResolveResult {
- public PyRatedCallee(@NotNull PyCallable callable, int rate) {
+ @NotNull
+ private final PyCallableType myCallableType;
+
+ public PyRatedCallee(@NotNull PyCallableType callableType, @Nullable PyCallable callable, int rate) {
super(rate, callable);
+ myCallableType = callableType;
+ }
+
+ @NotNull
+ public PyCallableType getCallableType() {
+ return myCallableType;
}
@Override
- @NotNull
+ @Nullable
public PyCallable getElement() {
//noinspection ConstantConditions
return (PyCallable)super.getElement();
@@ -508,7 +534,7 @@ public interface PyCallExpression extends PyCallSiteExpression {
}
@Override
- @NotNull
+ @Nullable
public PyCallable getElement() {
//noinspection ConstantConditions
return (PyCallable)super.getElement();
diff --git a/python/src/com/jetbrains/python/PyParameterInfoHandler.java b/python/src/com/jetbrains/python/PyParameterInfoHandler.java
index fbd3280f64ea..7ddc6fbf58a7 100644
--- a/python/src/com/jetbrains/python/PyParameterInfoHandler.java
+++ b/python/src/com/jetbrains/python/PyParameterInfoHandler.java
@@ -74,17 +74,15 @@ public class PyParameterInfoHandler implements ParameterInfoHandler ratedMarkedCallees =
- PyUtil.filterTopPriorityResults(call.multiResolveRatedCallee(resolveContext));
-
- final Object[] items = new Object[ratedMarkedCallees.size()];
- int currentPosition = 0;
- for (PyCallExpression.PyRatedMarkedCallee ratedMarkedCallee : ratedMarkedCallees) {
- items[currentPosition] = Pair.createNonNull(call, ratedMarkedCallee.getMarkedCallee());
- currentPosition++;
- }
-
- context.setItemsToShow(items);
+ context.setItemsToShow(
+ PyUtil
+ .filterTopPriorityResults(call.multiResolveRatedCallee(resolveContext))
+ .stream()
+ .map(PyCallExpression.PyRatedMarkedCallee::getMarkedCallee)
+ .filter(markedCallee -> markedCallee.getCallableType().getParameters(typeEvalContext) != null)
+ .map(markedCallee -> Pair.createNonNull(call, markedCallee))
+ .toArray()
+ );
return argumentList;
}
@@ -177,13 +175,14 @@ public class PyParameterInfoHandler implements ParameterInfoHandler parameters = markedCallee.getCallableType().getParameters(typeEvalContext);
+ if (parameters == null) return;
+
+ final PyCallExpression.PyArgumentsMapping mapping = PyCallExpressionHelper.mapArguments(callExpression, markedCallee, typeEvalContext);
+ if (mapping.getMarkedCallee() == null) return;
- final List parameters = PyUtil.getParameters(markedCallee.getCallable(), typeEvalContext);
final Map indexToNamedParameter = new HashMap<>(parameters.size());
// param -> hint index. indexes are not contiguous, because some hints are parentheses.
diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java
index bbfa68128e20..4dc94f61eec0 100644
--- a/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyArgumentListInspection.java
@@ -34,6 +34,7 @@ import com.jetbrains.python.inspections.quickfix.PyChangeSignatureQuickFix;
import com.jetbrains.python.inspections.quickfix.PyRemoveArgumentQuickFix;
import com.jetbrains.python.inspections.quickfix.PyRenameArgumentQuickFix;
import com.jetbrains.python.psi.*;
+import com.jetbrains.python.psi.impl.ParamHelper;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.types.*;
import com.jetbrains.python.refactoring.changeSignature.PyChangeSignatureHandler;
@@ -85,8 +86,10 @@ public class PyArgumentListInspection extends PyInspection {
final PyCallExpression.PyMarkedCallee markedCallee = deco.resolveCallee(getResolveContext());
if (markedCallee != null && !markedCallee.isImplicitlyResolved()) {
final PyCallable callable = markedCallee.getCallable();
+ if (callable == null) return;
final int firstParamOffset = markedCallee.getImplicitOffset();
- final List params = PyUtil.getParameters(callable, myTypeEvalContext);
+ final List params = markedCallee.getCallableType().getParameters(myTypeEvalContext);
+ if (params == null) return;
final PyCallableParameter allegedFirstParam = ContainerUtil.getOrElse(params, firstParamOffset - 1, null);
if (allegedFirstParam == null || allegedFirstParam.isKeywordContainer()) {
@@ -280,18 +283,22 @@ public class PyArgumentListInspection extends PyInspection {
.of(mappings)
.map(PyCallExpression.PyArgumentsMapping::getMarkedCallee)
.nonNull()
- .map(markedCallee -> calculatePossibleCalleeRepresentation(markedCallee.getCallable(), context))
+ .map(markedCallee -> calculatePossibleCalleeRepresentation(markedCallee, context))
+ .nonNull()
.collect(Collectors.joining("
"));
}
- @NotNull
- private static String calculatePossibleCalleeRepresentation(@NotNull PyCallable callable, @NotNull TypeEvalContext context) {
- final String name = callable.getName();
- final String parameters = callable.getParameterList().getPresentableText(true, context);
+ @Nullable
+ private static String calculatePossibleCalleeRepresentation(@NotNull PyCallExpression.PyMarkedCallee markedCallee, @NotNull TypeEvalContext context) {
+ final String name = markedCallee.getCallable() != null ? markedCallee.getCallable().getName() : "";
+ final List callableParameters = markedCallee.getCallableType().getParameters(context);
+ if (callableParameters == null) return null;
+
+ final String parameters = ParamHelper.getPresentableText(callableParameters, true, context);
final String callableNameAndParameters = name + parameters;
return Optional
- .ofNullable(PyUtil.as(callable, PyFunction.class))
+ .ofNullable(PyUtil.as(markedCallee.getCallable(), PyFunction.class))
.map(PyFunction::getContainingClass)
.map(PyClass::getName)
.map(className -> PyNames.INIT.equals(name) ? className + parameters : className + "." + callableNameAndParameters)
diff --git a/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java b/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java
index 4a4df4d305fc..da183c35f8fe 100644
--- a/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyCallByClassInspection.java
@@ -93,7 +93,7 @@ public class PyCallByClassInspection extends PyInspection {
final PyCallExpression.PyMarkedCallee markedCallee = mapping.getMarkedCallee();
if (markedCallee != null && markedCallee.getModifier() != STATICMETHOD) {
final PyCallableParameter firstParameter =
- ContainerUtil.getFirstItem(PyUtil.getParameters(markedCallee.getCallable(), myTypeEvalContext));
+ ContainerUtil.getFirstItem(markedCallee.getCallableType().getParameters(myTypeEvalContext));
if (firstParameter != null) {
for (Map.Entry entry : mapping.getMappedParameters().entrySet()) {
// we ignore *arg and **arg which we cannot analyze
diff --git a/python/src/com/jetbrains/python/psi/impl/ParamHelper.java b/python/src/com/jetbrains/python/psi/impl/ParamHelper.java
index ed96f2fe3ca2..9321d433e9e3 100644
--- a/python/src/com/jetbrains/python/psi/impl/ParamHelper.java
+++ b/python/src/com/jetbrains/python/psi/impl/ParamHelper.java
@@ -19,7 +19,9 @@ import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.PyCallableParameter;
import com.jetbrains.python.psi.types.PyCallableParameterImpl;
+import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -68,6 +70,57 @@ public class ParamHelper {
}
}
+ @NotNull
+ public static String getPresentableText(@NotNull PyParameter[] parameters,
+ boolean includeDefaultValue,
+ @Nullable TypeEvalContext context) {
+ return getPresentableText(ContainerUtil.map(parameters, PyCallableParameterImpl::new), includeDefaultValue, context);
+ }
+
+ @NotNull
+ public static String getPresentableText(@NotNull List parameters,
+ boolean includeDefaultValue,
+ @Nullable TypeEvalContext context) {
+ final StringBuilder result = new StringBuilder();
+ result.append("(");
+
+ walkDownParameters(
+ parameters,
+ new ParamHelper.ParamWalker() {
+ @Override
+ public void enterTupleParameter(PyTupleParameter param, boolean first, boolean last) {
+ result.append("(");
+ }
+
+ @Override
+ public void leaveTupleParameter(PyTupleParameter param, boolean first, boolean last) {
+ result.append(")");
+ if (!last) result.append(", ");
+ }
+
+ @Override
+ public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) {
+ visitNonPsiParameter(new PyCallableParameterImpl(param), first, last);
+ }
+
+ @Override
+ public void visitSingleStarParameter(PySingleStarParameter param, boolean first, boolean last) {
+ result.append('*');
+ if (!last) result.append(", ");
+ }
+
+ @Override
+ public void visitNonPsiParameter(@NotNull PyCallableParameter parameter, boolean first, boolean last) {
+ result.append(parameter.getPresentableText(includeDefaultValue, context));
+ if (!last) result.append(", ");
+ }
+ }
+ );
+
+ result.append(")");
+ return result.toString();
+ }
+
public interface ParamWalker {
/**
* Is called when a tuple parameter is encountered, before visiting any parameters nested in it.
diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java
index 3b4b33a7e5cb..c1ed6784dcd1 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java
@@ -132,8 +132,8 @@ public class PyCallExpressionHelper {
}
return forEveryScopeTakeOverloadsOtherwiseImplementations(ratedMarkedCallees, PyCallExpression.PyRatedMarkedCallee::getElement, context)
- // while clarifying resolve results we could get duplicate callables so we have to group them and select result with highest rate
- .collect(Collectors.groupingBy(markedCallee -> markedCallee.getElement(), LinkedHashMap::new, Collectors.toList()))
+ // while clarifying resolve results we could get duplicate callable types so we have to group them and select result with highest rate
+ .collect(Collectors.groupingBy(markedCallee -> markedCallee.getMarkedCallee().getCallableType(), LinkedHashMap::new, Collectors.toList()))
.entrySet()
.stream()
.map(entry -> entry.getValue().stream().max(Comparator.comparingInt(PyCallExpression.PyRatedMarkedCallee::getRate)).orElse(null))
@@ -198,8 +198,14 @@ public class PyCallExpressionHelper {
private static PyCallExpression.PyRatedMarkedCallee markResolveResult(@NotNull ClarifiedResolveResult resolveResult,
@NotNull TypeEvalContext context,
int implicitOffset) {
- if (resolveResult.myClarifiedResolved instanceof PyCallable) {
- final PyCallable callable = (PyCallable)resolveResult.myClarifiedResolved;
+ final PsiElement clarifiedResolved = resolveResult.myClarifiedResolved;
+ if (!(clarifiedResolved instanceof PyTypedElement)) return null;
+
+ final PyCallableType callableType = PyUtil.as(context.getType((PyTypedElement)clarifiedResolved), PyCallableType.class);
+ if (callableType == null) return null;
+
+ if (clarifiedResolved instanceof PyCallable) {
+ final PyCallable callable = (PyCallable)clarifiedResolved;
final PyFunction.Modifier originalModifier = callable instanceof PyFunction ? ((PyFunction)callable).getModifier() : null;
final PyFunction.Modifier resolvedModifier = ObjectUtils.chooseNotNull(originalModifier, resolveResult.myWrappedModifier);
@@ -218,6 +224,7 @@ public class PyCallExpressionHelper {
implicitOffset + getImplicitArgumentCount(callable, resolvedModifier, isConstructorCall, isByInstance, isByClass);
final PyCallExpression.PyMarkedCallee markedCallee = new PyCallExpression.PyMarkedCallee(
+ callableType,
callable,
resolvedModifier,
Math.max(0, resolvedImplicitOffset), // wrong source can trigger strange behaviour
@@ -227,7 +234,10 @@ public class PyCallExpressionHelper {
return new PyCallExpression.PyRatedMarkedCallee(markedCallee, resolveResult.myOriginalResolveResult.getRate());
}
- return null;
+ return new PyCallExpression.PyRatedMarkedCallee(
+ new PyCallExpression.PyMarkedCallee(callableType, null, null, implicitOffset, resolveResult.myOriginalResolveResult.isImplicit()),
+ resolveResult.myOriginalResolveResult.getRate()
+ );
}
/**
@@ -647,7 +657,9 @@ public class PyCallExpressionHelper {
@NotNull PyArgumentList argumentList,
@NotNull PyCallExpression.PyMarkedCallee markedCallee,
@NotNull TypeEvalContext context) {
- final List parameters = PyUtil.getParameters(markedCallee.getCallable(), context);
+ final List parameters = markedCallee.getCallableType().getParameters(context);
+ if (parameters == null) return PyCallExpression.PyArgumentsMapping.empty(callExpression);
+
final List explicitParameters = dropImplicitParameters(parameters, markedCallee.getImplicitOffset());
final List arguments = Arrays.asList(argumentList.getArguments());
final ArgumentMappingResults mappingResults = analyzeArguments(arguments, explicitParameters);
@@ -679,8 +691,7 @@ public class PyCallExpressionHelper {
public static ArgumentMappingResults mapArguments(@NotNull PyCallSiteExpression callSite,
@NotNull PyCallable callable,
@NotNull TypeEvalContext context) {
- final List parameters = PyUtil.getParameters(callable, context);
- return mapArguments(callSite, callable, parameters, context);
+ return mapArguments(callSite, callable, callable.getParameters(context), context);
}
@NotNull
@@ -879,7 +890,10 @@ public class PyCallExpressionHelper {
boolean containsImplementations = false;
for (E element : elements) {
- final boolean overload = PyiUtil.isOverload(mapper.apply(element), context);
+ final PsiElement mapped = mapper.apply(element);
+ if (mapped == null) continue;
+
+ final boolean overload = PyiUtil.isOverload(mapped, context);
containsOverloads |= overload;
containsImplementations |= !overload;
diff --git a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java
index 4b45067e4369..424e7e946d4e 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java
@@ -59,11 +59,13 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase im
return qname != null ? qname.getLastComponent() : null;
}
+ @Override
@Nullable
public PyFunction getTarget() {
return PsiTreeUtil.getParentOfType(this, PyFunction.class);
}
+ @Override
public boolean isBuiltin() {
ASTNode node = getNode().findChildByType(PythonDialectsTokenSetProvider.INSTANCE.getReferenceExpressionTokens());
if (node != null) {
@@ -74,11 +76,14 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase im
return false;
}
+ @Override
public boolean hasArgumentList() {
final ASTNode arglistNode = getNode().findChildByType(PyElementTypes.ARGUMENT_LIST);
return (arglistNode != null) && (arglistNode.findChildByType(PyTokenTypes.LPAR) != null);
}
+ @Override
+ @Nullable
public QualifiedName getQualifiedName() {
final PyDecoratorStub stub = getStub();
if (stub != null) {
@@ -93,6 +98,8 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase im
}
}
+ @Override
+ @Nullable
public PyExpression getCallee() {
try {
return (PyExpression)getFirstChild().getNextSibling(); // skip the @ before call
@@ -112,7 +119,8 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase im
if (!hasArgumentList()) {
// NOTE: that +1 thing looks fishy
final PyMarkedCallee oldMarkedCallee = ratedMarkedCallee.getMarkedCallee();
- final PyMarkedCallee newMarkedCallee = new PyMarkedCallee(oldMarkedCallee.getCallable(),
+ final PyMarkedCallee newMarkedCallee = new PyMarkedCallee(oldMarkedCallee.getCallableType(),
+ oldMarkedCallee.getCallable(),
oldMarkedCallee.getModifier(),
oldMarkedCallee.getImplicitOffset() + 1,
oldMarkedCallee.isImplicitlyResolved());
@@ -150,6 +158,8 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase im
}
}
+ @Override
+ @Nullable
public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
return PyCallExpressionHelper.getCallType(this, context);
}
diff --git a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java
index 82d330e8929f..a8fc92ca43e1 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java
@@ -27,7 +27,6 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PlatformIcons;
import com.intellij.util.Processor;
-import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
@@ -184,20 +183,7 @@ public class PyNamedParameterImpl extends PyBaseElementImpl
@Override
@NotNull
public String getPresentableText(boolean includeDefaultValue, @Nullable TypeEvalContext context) {
- final StringBuilder target = new StringBuilder();
- final String COMMA = ", ";
- target.append("(");
- ParamHelper.walkDownParamArray(
- getParameters(),
- new ParamHelper.ParamVisitor() {
- @Override
- public void enterTupleParameter(PyTupleParameter param, boolean first, boolean last) {
- target.append("(");
- }
-
- @Override
- public void leaveTupleParameter(PyTupleParameter param, boolean first, boolean last) {
- target.append(")");
- if (!last) target.append(COMMA);
- }
-
- @Override
- public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) {
- target.append(param.getRepr(includeDefaultValue, context));
- if (!last) target.append(COMMA);
- }
-
- @Override
- public void visitSingleStarParameter(PySingleStarParameter param, boolean first, boolean last) {
- target.append('*');
- if (!last) target.append(COMMA);
- }
- }
- );
- target.append(")");
- return target.toString();
+ return ParamHelper.getPresentableText(getParameters(), includeDefaultValue, context);
}
@Nullable
diff --git a/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java b/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java
index 87328d37b1f5..73457f17eefe 100644
--- a/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java
+++ b/python/src/com/jetbrains/python/psi/types/PyCallableParameterImpl.java
@@ -17,6 +17,7 @@ package com.jetbrains.python.psi.types;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.documentation.PythonDocumentationProvider;
import com.jetbrains.python.psi.*;
@@ -107,7 +108,8 @@ public class PyCallableParameterImpl implements PyCallableParameter {
if (isPositionalContainer()) sb.append("*");
else if (isKeywordContainer()) sb.append("**");
- sb.append(getName());
+ final String name = getName();
+ sb.append(name != null ? name : "...");
final PyType argumentType = context == null ? null : getArgumentType(context);
if (argumentType != null) {
@@ -142,8 +144,20 @@ public class PyCallableParameterImpl implements PyCallableParameter {
@Nullable
@Override
public PyType getArgumentType(@NotNull TypeEvalContext context) {
- final PyNamedParameter namedParameter = PyUtil.as(myElement, PyNamedParameter.class);
- return namedParameter == null ? null : namedParameter.getArgumentType(context);
+ final PyType parameterType = getType(context);
+
+ if (parameterType instanceof PyCollectionType) {
+ final PyCollectionType collectionType = (PyCollectionType)parameterType;
+
+ if (isPositionalContainer()) {
+ return collectionType.getIteratedItemType();
+ }
+ else if (isKeywordContainer()) {
+ return ContainerUtil.getOrElse(collectionType.getElementTypes(context), 1, null);
+ }
+ }
+
+ return parameterType;
}
@Override
diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java
index 8a8ad243a6cb..b05bfbd2d6dc 100644
--- a/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java
+++ b/python/src/com/jetbrains/python/psi/types/PyFunctionTypeImpl.java
@@ -28,7 +28,6 @@ import com.jetbrains.python.psi.resolve.RatedResolveResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -42,9 +41,11 @@ import static com.jetbrains.python.psi.PyUtil.as;
*/
public class PyFunctionTypeImpl implements PyFunctionType {
@NotNull private final PyCallable myCallable;
+ @NotNull private final List myCallableParameters;
public PyFunctionTypeImpl(@NotNull PyCallable callable) {
myCallable = callable;
+ myCallableParameters = ContainerUtil.map(callable.getParameterList().getParameters(), PyCallableParameterImpl::new);
}
@Nullable
@@ -62,11 +63,7 @@ public class PyFunctionTypeImpl implements PyFunctionType {
@Nullable
@Override
public List getParameters(@NotNull TypeEvalContext context) {
- final List result = new ArrayList<>();
- for (PyParameter parameter : myCallable.getParameterList().getParameters()) {
- result.add(new PyCallableParameterImpl(parameter));
- }
- return result;
+ return myCallableParameters;
}
@Override
diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java
index 2c929aa1aae8..c77d7aa8763f 100644
--- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java
+++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java
@@ -621,6 +621,7 @@ public class PyTypeChecker {
public static List analyzeCallSite(@NotNull PyCallSiteExpression callSite, @NotNull TypeEvalContext context) {
final List results = new ArrayList<>();
for (PyCallable callable : multiResolveCallee(callSite, context)) {
+ if (callable == null) continue;
final PyExpression receiver = getReceiver(callSite, callable);
final ArgumentMappingResults mapping = mapArguments(callSite, callable, context);
results.add(new AnalyzeCallResults(callable, receiver, mapping));
diff --git a/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java b/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java
index b2d316b012c9..7049da74d1fd 100644
--- a/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java
+++ b/python/src/com/jetbrains/python/pyi/PyiTypeProvider.java
@@ -17,7 +17,6 @@ package com.jetbrains.python.pyi;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
-import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyCallExpressionHelper;
import com.jetbrains.python.psi.types.*;
@@ -115,15 +114,17 @@ public class PyiTypeProvider extends PyTypeProviderBase {
final PyType returnType = context.getReturnType(overload);
allReturnTypes.add(PyTypeChecker.substitute(returnType, new HashMap<>(), context));
- final PyExpression receiver = PyTypeChecker.getReceiver(callSite, overload);
- final PyCallExpressionHelper.ArgumentMappingResults mapping = mapArguments(callSite, overload, context);
- if (mapping == null) {
+ final PyCallExpressionHelper.ArgumentMappingResults mapping = PyCallExpressionHelper.mapArguments(callSite, overload, context);
+ if (!mapping.getUnmappedArguments().isEmpty() || !mapping.getUnmappedParameters().isEmpty()) {
continue;
}
+
+ final PyExpression receiver = PyTypeChecker.getReceiver(callSite, overload);
final Map substitutions = PyTypeChecker.unifyGenericCall(receiver, mapping.getMappedParameters(), context);
if (substitutions == null) {
continue;
}
+
final PyType unifiedType = PyTypeChecker.substitute(returnType, substitutions, context);
matchedReturnTypes.add(unifiedType);
}
@@ -160,21 +161,4 @@ public class PyiTypeProvider extends PyTypeProviderBase {
}
return null;
}
-
- @Nullable
- private static PyCallExpressionHelper.ArgumentMappingResults mapArguments(@NotNull PyCallSiteExpression callSite,
- @NotNull PyFunction function,
- @NotNull TypeEvalContext context) {
- final List parameters =
- ContainerUtil.map(function.getParameterList().getParameters(), PyCallableParameterImpl::new);
-
- final PyCallExpressionHelper.ArgumentMappingResults mapping =
- PyCallExpressionHelper.mapArguments(callSite, function, parameters, context);
-
- if (!mapping.getUnmappedArguments().isEmpty() || !mapping.getUnmappedParameters().isEmpty()) {
- return null;
- }
-
- return mapping;
- }
}
diff --git a/python/testData/paramInfo/JustTypingCallable.py b/python/testData/paramInfo/JustTypingCallable.py
new file mode 100644
index 000000000000..20a526021c44
--- /dev/null
+++ b/python/testData/paramInfo/JustTypingCallable.py
@@ -0,0 +1,9 @@
+from typing import Callable
+
+
+def f() -> Callable:
+ pass
+
+
+c = f()
+print(c())
diff --git a/python/testData/paramInfo/TypingCallableWithKnownParameters.py b/python/testData/paramInfo/TypingCallableWithKnownParameters.py
new file mode 100644
index 000000000000..cd84864b6606
--- /dev/null
+++ b/python/testData/paramInfo/TypingCallableWithKnownParameters.py
@@ -0,0 +1,9 @@
+from typing import Callable
+
+
+def f() -> Callable[[int, str], int]:
+ pass
+
+
+c = f()
+print(c())
diff --git a/python/testData/paramInfo/TypingCallableWithUnknownParameters.py b/python/testData/paramInfo/TypingCallableWithUnknownParameters.py
new file mode 100644
index 000000000000..e5f1b231b9ec
--- /dev/null
+++ b/python/testData/paramInfo/TypingCallableWithUnknownParameters.py
@@ -0,0 +1,9 @@
+from typing import Callable
+
+
+def f() -> Callable[..., int]:
+ pass
+
+
+c = f()
+print(c())
diff --git a/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java b/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java
index c06179c45d66..5164fd8a81c3 100644
--- a/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java
+++ b/python/testSrc/com/jetbrains/python/PyParameterInfoTest.java
@@ -613,6 +613,43 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
feignCtrlP(offset).check("p: str=\"\\n\", t: str=\"\\t\", r: str=\"\\r\"", new String[]{"p: str=\"\\n\", "});
}
+ public void testJustTypingCallable() {
+ runWithLanguageLevel(
+ LanguageLevel.PYTHON35,
+ () -> {
+ final int offset = loadTest(1).get("").getTextOffset();
+
+ feignCtrlP(offset).check(Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
+ }
+ );
+ }
+
+ public void testTypingCallableWithUnknownParameters() {
+ runWithLanguageLevel(
+ LanguageLevel.PYTHON35,
+ () -> {
+ final int offset = loadTest(1).get("").getTextOffset();
+
+ feignCtrlP(offset).check(Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
+ }
+ );
+ }
+
+ public void testTypingCallableWithKnownParameters() {
+ runWithLanguageLevel(
+ LanguageLevel.PYTHON35,
+ () -> {
+
+ final int offset = loadTest(1).get("").getTextOffset();
+
+ final List texts = Collections.singletonList("...: int, ...: str");
+ final List highlighted = Collections.singletonList(new String[]{"...: int, "});
+
+ feignCtrlP(offset).check(texts, highlighted, Collections.singletonList(ArrayUtil.EMPTY_STRING_ARRAY));
+ }
+ );
+ }
+
/**
* Imitates pressing of Ctrl+P; fails if results are not as expected.
* @param offset offset of 'cursor' where Ctrl+P is pressed.