allow each instance of PyCallableType to tell whether it's actually callable or not; distinguish class definition and class instance for Java class types; correctly treat Java constructors as callable (PY-4269)

This commit is contained in:
Dmitry Jemerov
2013-02-27 16:09:37 +01:00
parent f4ad359267
commit 066ddf137d
8 changed files with 74 additions and 20 deletions
@@ -7,9 +7,11 @@ import com.intellij.psi.ResolveState;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.psi.AccessDirection;
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;
import com.jetbrains.python.psi.types.PyCallableType;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
@@ -20,11 +22,13 @@ import java.util.List;
/**
* @author yole
*/
public class PyJavaClassType implements PyType {
public class PyJavaClassType implements PyCallableType {
private final PsiClass myClass;
private final boolean myDefinition;
public PyJavaClassType(final PsiClass aClass) {
public PyJavaClassType(final PsiClass aClass, boolean definition) {
myClass = aClass;
myDefinition = definition;
}
@Nullable
@@ -68,4 +72,18 @@ public class PyJavaClassType implements PyType {
@Override
public void assertValid(String message) {
}
@Override
public boolean isCallable() {
return myDefinition;
}
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) {
if (myDefinition) {
return new PyJavaClassType(myClass, false);
}
return null;
}
}
@@ -23,7 +23,7 @@ public class PyJavaTypeProvider extends PyTypeProviderBase {
@Nullable
public PyType getReferenceType(@NotNull final PsiElement referenceTarget, TypeEvalContext context, @Nullable PsiElement anchor) {
if (referenceTarget instanceof PsiClass) {
return new PyJavaClassType((PsiClass) referenceTarget);
return new PyJavaClassType((PsiClass) referenceTarget, true);
}
if (referenceTarget instanceof PsiPackage) {
return new PyJavaPackageType((PsiPackage) referenceTarget, anchor == null ? null : ModuleUtil.findModuleForPsiElement(anchor));
@@ -44,7 +44,7 @@ public class PyJavaTypeProvider extends PyTypeProviderBase {
final PsiClassType classType = (PsiClassType)type;
final PsiClass psiClass = classType.resolve();
if (psiClass != null) {
return new PyJavaClassType(psiClass);
return new PyJavaClassType(psiClass, false);
}
}
return null;
@@ -67,7 +67,7 @@ public class PyJavaTypeProvider extends PyTypeProviderBase {
if (paramType instanceof PsiClassType) {
final PsiClass psiClass = ((PsiClassType)paramType).resolve();
if (psiClass != null) {
superMethodParameterTypes.add(new PyJavaClassType(psiClass));
superMethodParameterTypes.add(new PyJavaClassType(psiClass, false));
}
}
}
@@ -0,0 +1,22 @@
package com.jetbrains.jython;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import com.jetbrains.python.PythonTestUtil;
import com.jetbrains.python.inspections.PyCallingNonCallableInspection;
/**
* @author yole
*/
public class PyJythonHighlightingTest extends LightCodeInsightFixtureTestCase {
public void testCallableJavaClass() {
myFixture.configureByFile("callableJavaClass.py");
myFixture.enableInspections(PyCallingNonCallableInspection.class);
myFixture.checkHighlighting(true, false, false);
}
@Override
protected String getTestDataPath() {
return PythonTestUtil.getTestDataPath() + "/highlighting/jython/";
}
}
@@ -11,6 +11,11 @@ import org.jetbrains.annotations.Nullable;
* @author yole
*/
public interface PyCallableType extends PyType {
/**
* Returns true if the type is callable.
*/
boolean isCallable();
/**
* Returns the type which is the result of calling an instance of this type.
*
@@ -220,6 +220,21 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
return PyBuiltinCache.getInstance(myClass).getObjectType("type");
}
@Override
public boolean isCallable() {
if (isDefinition()) {
return true;
}
if (PyTypeChecker.isMethodType(this)) {
return true;
}
final PyClass cls = getPyClass();
if (PyABCUtil.isSubclass(cls, PyNames.CALLABLE)) {
return true;
}
return false;
}
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) {
@@ -22,6 +22,11 @@ public class PyFunctionType implements PyCallableType {
myCallable = callable;
}
@Override
public boolean isCallable() {
return true;
}
@Nullable
@Override
public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) {
@@ -427,19 +427,6 @@ public class PyTypeChecker {
if (type == null || type instanceof PyTypeReference) {
return null;
}
else if (type instanceof PyClassType) {
final PyClassType classType = (PyClassType)type;
if (classType.isDefinition()) {
return true;
}
if (isMethodType(classType)) {
return true;
}
final PyClass cls = classType.getPyClass();
if (PyABCUtil.isSubclass(cls, PyNames.CALLABLE)) {
return true;
}
}
else if (type instanceof PyUnionType) {
Boolean result = true;
for (PyType member : ((PyUnionType)type).getMembers()) {
@@ -454,12 +441,12 @@ public class PyTypeChecker {
return result;
}
else if (type instanceof PyCallableType) {
return true;
return ((PyCallableType) type).isCallable();
}
return false;
}
private static boolean isMethodType(@NotNull PyClassType type) {
public static boolean isMethodType(@NotNull PyClassType type) {
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(type.getPyClass());
return type.equals(builtinCache.getClassMethodType()) || type.equals(builtinCache.getStaticMethodType());
}
@@ -0,0 +1,2 @@
from java.util import ArrayList
l = ArrayList()