PY-17052 Union is callable if at least one union member is callable
authorIlya.Kazakevich <Ilya.Kazakevich@jetbrains.com>
Mon, 5 Oct 2015 17:26:07 +0000 (20:26 +0300)
committerIlya.Kazakevich <Ilya.Kazakevich@jetbrains.com>
Mon, 5 Oct 2015 18:30:58 +0000 (21:30 +0300)
 * This change breaks PyCallingNonCallableInspectionTest but it is PY-17112 bug

python/src/com/jetbrains/python/psi/types/PyTypeChecker.java
python/testData/inspections/PyCallingNonCallableInspection/unionType.py

index 41a614abd97dce8289547769b1f6688376f79fa3..7452f0d8b494fa98632114aa48864124781a6cda 100644 (file)
@@ -517,28 +517,37 @@ public class PyTypeChecker {
     if (type == null) {
       return null;
     }
-    else if (type instanceof PyUnionType) {
-      Boolean result = true;
-      for (PyType member : ((PyUnionType)type).getMembers()) {
-        final Boolean callable = isCallable(member);
-        if (callable == null) {
-          return null;
-        }
-        else if (!callable) {
-          result = false;
-        }
-      }
-      return result;
+    if (type instanceof PyUnionType) {
+      return isUnionCallable((PyUnionType)type);
     }
-    else if (type instanceof PyCallableType) {
+    if (type instanceof PyCallableType) {
       return ((PyCallableType) type).isCallable();
     }
-    else if (type instanceof PyStructuralType && ((PyStructuralType)type).isInferredFromUsages()) {
+    if (type instanceof PyStructuralType && ((PyStructuralType)type).isInferredFromUsages()) {
       return true;
     }
     return false;
   }
 
+  /**
+   * If at least one is callable -- it is callable.
+   * If at least one is unknown -- it is unknown.
+   * It is false otherwise.
+   */
+  @Nullable
+  private static Boolean isUnionCallable(@NotNull final PyUnionType type) {
+    for (final PyType member : type.getMembers()) {
+      final Boolean callable = isCallable(member);
+      if (callable == null) {
+        return null;
+      }
+      if (callable) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   public static boolean overridesGetAttr(@NotNull PyClass cls, @NotNull TypeEvalContext context) {
     PsiElement method = resolveClassMember(cls, PyNames.GETATTR, context);
     if (method != null) {
index 055caf7f431985b5848c0e429af73b1767570092..dfd35a33eaae8e87811a927a9e466c42bad3d960 100644 (file)
@@ -5,4 +5,5 @@ class B(object):
 def foo(c):
     x = A if c else B
     y = A if c else 10
-    return x(), <warning descr="'y' is not callable">y()</warning>
+    z = A() if c else 10
+    return x(), y(), <warning descr="'z' is not callable">z()</warning>