Fixed resolving operator references on classes to their metaclasses (PY-16146)
authorAndrey Vlasovskikh <andrey.vlasovskikh@jetbrains.com>
Tue, 15 Sep 2015 21:44:22 +0000 (00:44 +0300)
committerAndrey Vlasovskikh <andrey.vlasovskikh@jetbrains.com>
Tue, 15 Sep 2015 21:44:22 +0000 (00:44 +0300)
python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
python/src/com/jetbrains/python/psi/impl/references/PyOperatorReference.java
python/testData/inspections/PyTypeCheckerInspection/TypingListSubscriptionExpression.py [new file with mode: 0644]
python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedSubscriptionOnClass.py [new file with mode: 0644]
python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java
python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java

index c14ddd47642779edb746812e1d845efd2f1d5c89..c32f047ea72290db29327a78c737f5731cc427e0 100644 (file)
@@ -534,10 +534,18 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
                 return;
               }
               addCreateMemberFromUsageFixes(type, reference, refText, actions);
-              if (type instanceof PyClassTypeImpl) {
+              if (type instanceof PyClassType) {
                 if (reference instanceof PyOperatorReference) {
+                  String className = type.getName();
+                  final PyClassType classType = (PyClassType)type;
+                  if (classType.isDefinition()) {
+                    final PyClassLikeType metaClassType = classType.getMetaClassType(myTypeEvalContext, true);
+                    if (metaClassType != null) {
+                      className = metaClassType.getName();
+                    }
+                  }
                   description = PyBundle.message("INSP.unresolved.operator.ref",
-                                                 type.getName(), refName,
+                                                 className, refName,
                                                  ((PyOperatorReference)reference).getReadableOperatorName());
                 }
                 else {
index 9a6ff12a12df49e6672032b99d9de87a32f508ae..d1c43ee1a4ecc5cb6a3ecd096c57a9425da2a99a 100644 (file)
@@ -22,6 +22,7 @@ import com.jetbrains.python.PyNames;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.resolve.PyResolveContext;
 import com.jetbrains.python.psi.resolve.RatedResolveResult;
+import com.jetbrains.python.psi.types.PyClassLikeType;
 import com.jetbrains.python.psi.types.PyClassType;
 import com.jetbrains.python.psi.types.PyType;
 import com.jetbrains.python.psi.types.TypeEvalContext;
@@ -142,8 +143,13 @@ public class PyOperatorReference extends PyReferenceImpl {
     final ArrayList<RatedResolveResult> results = new ArrayList<RatedResolveResult>();
     if (object != null && name != null) {
       final TypeEvalContext typeEvalContext = myContext.getTypeEvalContext();
-      final PyType type = typeEvalContext.getType(object);
+      PyType type = typeEvalContext.getType(object);
       typeEvalContext.trace("Side text is %s, type is %s", object.getText(), type);
+      if (type instanceof PyClassLikeType) {
+        if (((PyClassLikeType)type).isDefinition()) {
+          type = ((PyClassLikeType)type).getMetaClassType(typeEvalContext, true);
+        }
+      }
       if (type != null) {
         List<? extends RatedResolveResult> res = type.resolveMember(name, object, AccessDirection.of(myElement), myContext);
         if (res != null && res.size() > 0) {
diff --git a/python/testData/inspections/PyTypeCheckerInspection/TypingListSubscriptionExpression.py b/python/testData/inspections/PyTypeCheckerInspection/TypingListSubscriptionExpression.py
new file mode 100644 (file)
index 0000000..242a41d
--- /dev/null
@@ -0,0 +1,7 @@
+from typing import List, Any
+
+
+def f(x1: List[str],
+      x2: List['str'],
+      x3: List[Any]) -> None:
+    pass
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedSubscriptionOnClass.py b/python/testData/inspections/PyUnresolvedReferencesInspection/unresolvedSubscriptionOnClass.py
new file mode 100644 (file)
index 0000000..773679e
--- /dev/null
@@ -0,0 +1,5 @@
+class Foo(object):
+    def __getitem__(self, item):
+        return item
+
+Foo<warning descr="Class 'type' does not define '__getitem__', so the '[]' operator cannot be used on its instances">[</warning>0]
index 85ca91416a5047299e2240601d322a15489da9e0..b3fc9efa0b0eb813a97f6bdc0c2a146f0264e263 100644 (file)
@@ -73,4 +73,9 @@ public class Py3TypeCheckerInspectionTest extends PyTestCase {
   public void testTypingIterableForLoop() {
     doTest();
   }
+
+  // PY-16146
+  public void testTypingListSubscriptionExpression() {
+    doTest();
+  }
 }
index 59a93465d995137632c6d2418064d68b6222605e..eea37a2e7b6ab7a5302e3e4ac21a92fd77389bcc 100644 (file)
@@ -510,7 +510,10 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase {
     doTest();
   }
 
-
+  // PY-16146
+  public void testUnresolvedSubscriptionOnClass() {
+    doTest();
+  }
 
   @NotNull
   @Override