Introduced PyCallSiteExpression for distinguishing a[b] and a[b](c) in the type checker (PY-13051)

PyTypeChecker.analyzeCallSite() was used for analyzing the return type
of the call in the same way for different expressions: a[b] and a[b](c)
as analyzeCallSite(a[b]) making it impossible to distinguish between the
two.

Now we pass a PyCallExpression for analyzing the function call instead
of a PyQualifiedExpression.
This commit is contained in:
Andrey Vlasovskikh
2014-07-04 13:56:42 +04:00
parent 96936c9308
commit 63f2789931
24 changed files with 73 additions and 56 deletions
@@ -18,8 +18,8 @@ package com.jetbrains.python.psi.impl;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.psi.AccessDirection;
import com.jetbrains.python.psi.PyCallSiteExpression;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyQualifiedExpression;
import com.jetbrains.python.psi.resolve.CompletionVariantsProcessor;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
@@ -113,7 +113,7 @@ public class PyJavaClassType implements PyClassLikeType {
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) {
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
return getReturnType(context);
}
@@ -21,8 +21,8 @@ import com.intellij.psi.PsiMethod;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.psi.AccessDirection;
import com.jetbrains.python.psi.PyCallSiteExpression;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyQualifiedExpression;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.types.PyCallableParameter;
@@ -58,7 +58,7 @@ public class PyJavaMethodType implements PyCallableType {
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) {
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
return getReturnType(context);
}
@@ -45,7 +45,7 @@ public interface Callable extends PyTypedElement, PyQualifiedNameOwner {
* Returns the type of the call to the callable.
*/
@Nullable
PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite);
PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite);
/**
* Returns the type of the call to the callable where the call site is specified by the optional receiver and the arguments to parameters
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.Nullable;
/**
* @author yole
*/
public interface PyBinaryExpression extends PyQualifiedExpression, PyReferenceOwner {
public interface PyBinaryExpression extends PyQualifiedExpression, PyCallSiteExpression, PyReferenceOwner {
PyExpression getLeftExpression();
@Nullable PyExpression getRightExpression();
@@ -25,7 +25,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents an entire call expression, like <tt>foo()</tt> or <tt>foo.bar[1]('x')</tt>.
*/
public interface PyCallExpression extends PyExpression {
public interface PyCallExpression extends PyCallSiteExpression {
/**
* @return the expression representing the object being called (reference to a function).
@@ -0,0 +1,24 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.psi;
/**
* Marker interface for Python expressions that are call sites for explicit or implicit function calls.
*
* @author vlan
*/
public interface PyCallSiteExpression extends PyExpression {
}
@@ -21,7 +21,7 @@ import org.jetbrains.annotations.Nullable;
/**
* @author yole
*/
public interface PySubscriptionExpression extends PyQualifiedExpression, PyReferenceOwner {
public interface PySubscriptionExpression extends PyQualifiedExpression, PyCallSiteExpression, PyReferenceOwner {
/**
* @return For <code>spam[x][y][n]</code> will return <code>spam</code> regardless number of its dimensions
@@ -42,7 +42,7 @@ public interface PyTypeProvider {
PyType getReturnType(@NotNull Callable callable, @NotNull TypeEvalContext context);
@Nullable
PyType getCallType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context);
PyType getCallType(@NotNull PyFunction function, @Nullable PyCallSiteExpression callSite, @NotNull TypeEvalContext context);
@Nullable
PyType getContextManagerVariableType(PyClass contextManager, PyExpression withExpression, TypeEvalContext context);
@@ -15,7 +15,7 @@
*/
package com.jetbrains.python.psi.types;
import com.jetbrains.python.psi.PyQualifiedExpression;
import com.jetbrains.python.psi.PyCallSiteExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -46,7 +46,7 @@ public interface PyCallableType extends PyType {
* Returns the type which is the result of calling an instance of this type.
*/
@Nullable
PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite);
PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite);
/**
* Returns the list of parameter types.
@@ -35,7 +35,7 @@ public class PyTypeProviderBase implements PyTypeProvider {
protected interface ReturnTypeCallback {
@Nullable
PyType getType(@Nullable PyQualifiedExpression callSite, @Nullable PyType qualifierType, TypeEvalContext context);
PyType getType(@Nullable PyCallSiteExpression callSite, @Nullable PyType qualifierType, TypeEvalContext context);
}
private static class ReturnTypeDescriptor {
@@ -46,12 +46,13 @@ public class PyTypeProviderBase implements PyTypeProvider {
}
@Nullable
public PyType get(PyFunction function, @Nullable PyQualifiedExpression callSite, TypeEvalContext context) {
public PyType get(PyFunction function, @Nullable PyCallSiteExpression callSite, TypeEvalContext context) {
PyClass containingClass = function.getContainingClass();
if (containingClass != null) {
final ReturnTypeCallback typeCallback = myStringToReturnTypeMap.get(containingClass.getQualifiedName());
if (typeCallback != null) {
final PyExpression qualifier = callSite != null ? callSite.getQualifier() : null;
final PyExpression callee = callSite instanceof PyCallExpression ? ((PyCallExpression)callSite).getCallee() : null;
final PyExpression qualifier = callee instanceof PyQualifiedExpression ? ((PyQualifiedExpression)callee).getQualifier() : null;
PyType qualifierType = qualifier != null ? context.getType(qualifier) : null;
return typeCallback.getType(callSite, qualifierType, context);
}
@@ -62,7 +63,7 @@ public class PyTypeProviderBase implements PyTypeProvider {
private final ReturnTypeCallback mySelfTypeCallback = new ReturnTypeCallback() {
@Override
public PyType getType(@Nullable PyQualifiedExpression callSite, @Nullable PyType qualifierType, TypeEvalContext context) {
public PyType getType(@Nullable PyCallSiteExpression callSite, @Nullable PyType qualifierType, TypeEvalContext context) {
if (qualifierType instanceof PyClassType) {
PyClass aClass = ((PyClassType)qualifierType).getPyClass();
return PyPsiFacade.getInstance(aClass.getProject()).createClassType(aClass, false);
@@ -101,7 +102,7 @@ public class PyTypeProviderBase implements PyTypeProvider {
}
@Override
public PyType getCallType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) {
public PyType getCallType(@NotNull PyFunction function, @Nullable PyCallSiteExpression callSite, @NotNull TypeEvalContext context) {
ReturnTypeDescriptor descriptor;
synchronized (myMethodToReturnTypeMap) {
descriptor = myMethodToReturnTypeMap.get(function.getName());
@@ -20,10 +20,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.jetbrains.numpy.documentation.NumPyDocString;
import com.jetbrains.numpy.documentation.NumPyDocStringParameter;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyNamedParameter;
import com.jetbrains.python.psi.PyPsiFacade;
import com.jetbrains.python.psi.PyQualifiedExpression;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.PyTypeProviderBase;
import com.jetbrains.python.psi.types.TypeEvalContext;
@@ -67,9 +64,10 @@ public class NumpyDocStringTypeProvider extends PyTypeProviderBase {
@Nullable
@Override
public PyType getCallType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) {
public PyType getCallType(@NotNull PyFunction function, @Nullable PyCallSiteExpression callSite, @NotNull TypeEvalContext context) {
if (isInsideNumPy(function)) {
final NumPyDocString docString = NumPyDocString.forFunction(function, callSite);
final PyExpression callee = callSite instanceof PyCallExpression ? ((PyCallExpression)callSite).getCallee() : null;
final NumPyDocString docString = NumPyDocString.forFunction(function, callee);
if (docString != null) {
final List<NumPyDocStringParameter> returns = docString.getReturns();
final PyPsiFacade facade = getPsiFacade(function);
@@ -95,7 +95,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) {
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
if (myDefinitionLevel > 0) {
return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, myDefinitionLevel-1);
}
@@ -120,7 +120,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase {
@Nullable
@Override
public PyType getCallType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) {
public PyType getCallType(@NotNull PyFunction function, @Nullable PyCallSiteExpression callSite, @NotNull TypeEvalContext context) {
final String qname = getQualifiedName(function, callSite);
if (qname != null) {
if (OPEN_FUNCTIONS.contains(qname) && callSite != null) {
@@ -56,10 +56,7 @@ public class PyTypeCheckerInspection extends PyInspection {
// TODO: Visit decorators with arguments
@Override
public void visitPyCallExpression(PyCallExpression node) {
final PyExpression callee = node.getCallee();
if (callee instanceof PyQualifiedExpression) {
checkCallSite((PyQualifiedExpression)callee);
}
checkCallSite(node);
}
@Override
@@ -85,7 +82,7 @@ public class PyTypeCheckerInspection extends PyInspection {
}
}
private void checkCallSite(@Nullable PyQualifiedExpression callSite) {
private void checkCallSite(@Nullable PyCallSiteExpression callSite) {
final Map<PyGenericType, PyType> substitutions = new LinkedHashMap<PyGenericType, PyType>();
final PyTypeChecker.AnalyzeCallResults results = PyTypeChecker.analyzeCallSite(callSite, myTypeEvalContext);
if (results != null) {
@@ -449,7 +449,7 @@ public class PyCallExpressionHelper {
}
}
if (init != null) {
final PyType t = init.getCallType(context, (PyReferenceExpression)callee);
final PyType t = init.getCallType(context, call);
if (cls != null) {
if (init.getContainingClass() != cls) {
if (t instanceof PyCollectionType) {
@@ -474,11 +474,11 @@ public class PyCallExpressionHelper {
}
final PyType providedType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(target, context, call);
if (providedType instanceof PyCallableType) {
return ((PyCallableType)providedType).getCallType(context, (PyReferenceExpression)callee);
return ((PyCallableType)providedType).getCallType(context, call);
}
if (target instanceof Callable) {
final Callable callable = (Callable)target;
return callable.getCallType(context, (PyReferenceExpression)callee);
return callable.getCallType(context, call);
}
}
}
@@ -489,13 +489,7 @@ public class PyCallExpressionHelper {
final PyType type = context.getType(callee);
if (type instanceof PyCallableType) {
final PyCallableType callableType = (PyCallableType)type;
final PyQualifiedExpression callSite = callee instanceof PyQualifiedExpression ? (PyQualifiedExpression)callee : null;
if (callSite != null) {
return callableType.getCallType(context, callSite);
}
else {
return callableType.getReturnType(context);
}
return callableType.getCallType(context, call);
}
return null;
}
@@ -209,7 +209,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) {
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
PyType type = null;
for (PyTypeProvider typeProvider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
type = typeProvider.getCallType(this, callSite, context);
@@ -70,7 +70,7 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) {
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
return context.getReturnType(this);
}
@@ -88,7 +88,8 @@ public class PyPrefixExpressionImpl extends PyElementImpl implements PyPrefixExp
if (ref != null) {
final PsiElement resolved = ref.resolve();
if (resolved instanceof Callable) {
return ((Callable)resolved).getCallType(context, this);
// TODO: Make PyPrefixExpression a PyCallSiteExpression, use getCallType() here and analyze it in PyTypeChecker.analyzeCallSite()
return ((Callable)resolved).getReturnType(context, key);
}
}
return null;
@@ -21,8 +21,8 @@ import com.intellij.util.Function;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.AccessDirection;
import com.jetbrains.python.psi.PyCallSiteExpression;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyQualifiedExpression;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import org.jetbrains.annotations.NotNull;
@@ -55,7 +55,7 @@ public class PyCallableTypeImpl implements PyCallableType {
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) {
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
return myReturnType;
}
@@ -316,7 +316,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) {
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
return getReturnType(context);
}
@@ -52,7 +52,7 @@ public class PyFunctionType implements PyCallableType {
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyQualifiedExpression callSite) {
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
return myCallable.getCallType(context, callSite);
}
@@ -496,16 +496,9 @@ public class PyTypeChecker {
}
@Nullable
public static AnalyzeCallResults analyzeCallSite(@Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) {
if (callSite == null) {
return null;
}
PsiElement parent = callSite.getParent();
while (parent instanceof PyParenthesizedExpression) {
parent = ((PyParenthesizedExpression)parent).getContainedExpression();
}
if (parent instanceof PyCallExpression) {
return analyzeCall((PyCallExpression)parent, context);
public static AnalyzeCallResults analyzeCallSite(@Nullable PyCallSiteExpression callSite, @NotNull TypeEvalContext context) {
if (callSite instanceof PyCallExpression) {
return analyzeCall((PyCallExpression)callSite, context);
}
else if (callSite instanceof PyBinaryExpression) {
return analyzeCall((PyBinaryExpression)callSite, context);
@@ -0,0 +1,4 @@
ops = {'and': all, 'or': any}
op = ops['or']
ops['and']()
op()
@@ -89,6 +89,11 @@ public class PyCallingNonCallableInspectionTest extends PyTestCase {
doTest();
}
// PY-13051
public void testCallDictSubscriptionExpression() {
doTest();
}
private void doTest() {
setLanguageLevel(LanguageLevel.PYTHON27);
try {