allow each instance of PyCallableType to tell whether it's actually callable or not...
authorDmitry Jemerov <yole@jetbrains.com>
Wed, 27 Feb 2013 15:07:05 +0000 (16:07 +0100)
committerDmitry Jemerov <yole@jetbrains.com>
Wed, 27 Feb 2013 15:09:37 +0000 (16:09 +0100)
python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java
python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java
python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java [new file with mode: 0644]
python/psi-api/src/com/jetbrains/python/psi/types/PyCallableType.java
python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java
python/src/com/jetbrains/python/psi/types/PyFunctionType.java
python/src/com/jetbrains/python/psi/types/PyTypeChecker.java
python/testData/highlighting/jython/callableJavaClass.py [new file with mode: 0644]

index d1667a07eed53b6b618c5c6c4465310caf12767c..c9d212cb6ddd8df695b55e9b6017b6f0eda65697 100644 (file)
@@ -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;
+  }
 }
index 369894203038952ed1c0cb3e3c1b332694c73d1a..9d186f315c9ecad64cd000e696eadd213cdc6a49 100644 (file)
@@ -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));
               }
             }
           }
diff --git a/python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java b/python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java
new file mode 100644 (file)
index 0000000..8ddd01f
--- /dev/null
@@ -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/";
+  }
+}
index 6c594c0755a28b946b1cb6436539cfae7562b670..74d0c2e8637c2aef4a6587a2da9fa73a612e5bea 100644 (file)
@@ -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.
    *
index 0fb2f10830ebb61d516fe5f6850089e4a1f5d6c9..14adb35a719c04cf1a2036024f81c9316cb1725b 100644 (file)
@@ -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) {
index 8b68d03094a8eb431800c6af7a40e06c401fb89e..3bbfce984a95aac0dacb7997b10cc90a5e42c332 100644 (file)
@@ -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) {
index 80ff2bb3b929e2615577d7c2c632afa0f6a2df8b..5037de76a22557a3be976e3028c9031957ebd41a 100644 (file)
@@ -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());
   }
diff --git a/python/testData/highlighting/jython/callableJavaClass.py b/python/testData/highlighting/jython/callableJavaClass.py
new file mode 100644 (file)
index 0000000..eb4f6d1
--- /dev/null
@@ -0,0 +1,2 @@
+from java.util import ArrayList
+l = ArrayList()