From 69688bf2caa7b612aa196982595cae25d439ffea Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 26 Apr 2012 09:40:00 +0200 Subject: [PATCH] correct semantics of __slots__ with inheritance (PY-5939) --- .../PyUnresolvedReferencesInspection.java | 18 ++++++++++++++++-- .../slotsSubclass.py | 15 +++++++++++++++ .../PyUnresolvedReferencesInspectionTest.java | 4 ++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/slotsSubclass.py diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 53b14f82241d..676dba6b0d17 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -154,10 +154,24 @@ public class PyUnresolvedReferencesInspection extends PyInspection { final PyClass pyClass = ((PyClassType)type).getPyClass(); if (pyClass != null && pyClass.isNewStyleClass()) { final List slots = pyClass.getSlots(); - if (slots != null && !slots.contains(node.getReferencedName()) && !slots.contains("__dict__")) { + final String attrName = node.getReferencedName(); + if (slots != null && !slots.contains(attrName) && !slots.contains(PyNames.DICT)) { + for (PyClassRef ref : pyClass.iterateAncestors()) { + final PyClass ancestor = ref.getPyClass(); + if (ancestor == null) { + return; + } + if (PyNames.OBJECT.equals(ancestor.getName())) { + break; + } + final List ancestorSlots = ancestor.getSlots(); + if (ancestorSlots == null || ancestorSlots.contains(attrName) || ancestorSlots.contains(PyNames.DICT)) { + return; + } + } final ASTNode nameNode = node.getNameElement(); final PsiElement e = nameNode != null ? nameNode.getPsi() : node; - registerProblem(e, "'" + pyClass.getName() + "' object has no attribute '" + node.getReferencedName() + "'"); + registerProblem(e, "'" + pyClass.getName() + "' object has no attribute '" + attrName + "'"); } } } diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/slotsSubclass.py b/python/testData/inspections/PyUnresolvedReferencesInspection/slotsSubclass.py new file mode 100644 index 000000000000..dfd3f5e4cc50 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/slotsSubclass.py @@ -0,0 +1,15 @@ +class A(object): + __slots__ = ['a', 'b'] + def __init__(self): + self.a = None # <- all ok here + self.b = None # <- all ok here + +class C(A): + __slots__ = ['c', 'd'] + + def __init__(self, c): + super(C, self).__init__() + self.c = c + self.d = self.b + if self.c: + self.a = 10 diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index d9b498db8d5b..b74ba65d5de4 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -32,6 +32,10 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + public void testSlotsSubclass() { // PY-5939 + doTest(); + } + public void testImportExceptImportError() { doTest(); }