From fb578301b58a1714af775b7cab440fb8ad643e46 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 28 Jan 2013 22:05:33 +0400 Subject: [PATCH] Support for @DynamicAttrs annotation in the docstring of a class (PY-4600) --- .../PyUnresolvedReferencesInspection.java | 30 +++++++++++++++---- .../dynamicAttrsAnnotation.py | 26 ++++++++++++++++ .../PyUnresolvedReferencesInspectionTest.java | 5 ++++ 3 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/dynamicAttrsAnnotation.py diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index b12c5a4371fe..be43ea86172f 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -658,14 +658,9 @@ public class PyUnresolvedReferencesInspection extends PyInspection { if (hasUnresolvedAncestors(cls)) { return true; } - if (cls.getDecoratorList() != null) { + if (isDecoratedAsDynamic(cls, true)) { return true; } - for (PyClass base : cls.iterateAncestorClasses()) { - if (base.getDecoratorList() != null) { - return true; - } - } } if (qtype instanceof CythonBuiltinType || (qtype instanceof CythonType && reference instanceof PyOperatorReference)) { @@ -680,6 +675,29 @@ public class PyUnresolvedReferencesInspection extends PyInspection { return false; } + private static boolean isDecoratedAsDynamic(@NotNull PyClass cls, boolean inherited) { + if (inherited) { + if (isDecoratedAsDynamic(cls, false)) { + return true; + } + for (PyClass base : cls.iterateAncestorClasses()) { + if (base != null && isDecoratedAsDynamic(base, false)) { + return true; + } + } + } + else { + if (cls.getDecoratorList() != null) { + return true; + } + final String docString = cls.getDocStringValue(); + if (docString != null && docString.indexOf("@DynamicAttrs") != -1) { + return true; + } + } + return false; + } + private static boolean hasUnresolvedAncestors(PyClass cls) { for (PyClassRef classRef : cls.iterateAncestors()) { if (classRef.getPyClass() == null && classRef.getType() == null) { diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/dynamicAttrsAnnotation.py b/python/testData/inspections/PyUnresolvedReferencesInspection/dynamicAttrsAnnotation.py new file mode 100644 index 000000000000..dc42470ead4b --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/dynamicAttrsAnnotation.py @@ -0,0 +1,26 @@ +class B(object): + def foo(self): + pass + + +class C(object): + """ + @DynamicAttrs + """ + def bar(self): + pass + + +class D(C): + def baz(self): + pass + + +b = B() +b.foo(), b.spam() + +c = C() +c.bar(), c.spam() + +d = D() +d.bar(), d.baz(), d.spam(), d.eggs() diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 9a55669d630c..03fe96971412 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -237,6 +237,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + // PY-4600 + public void testDynamicAttrsAnnotation() { + doTest(); + } + private void doTest() { myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py"); myFixture.enableInspections(PyUnresolvedReferencesInspection.class);