mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
@@ -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);
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
ops = {'and': all, 'or': any}
|
||||
op = ops['or']
|
||||
ops['and']()
|
||||
op()
|
||||
+5
@@ -89,6 +89,11 @@ public class PyCallingNonCallableInspectionTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-13051
|
||||
public void testCallDictSubscriptionExpression() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
setLanguageLevel(LanguageLevel.PYTHON27);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user