PY-29717 Fix docstring inheritance for constructors and local classes

Also, add more tests on this feature in general.
This commit is contained in:
Mikhail Golubev
2018-05-30 18:45:35 +03:00
parent bfbf0a083a
commit ca372eb8ca
13 changed files with 58 additions and 15 deletions

View File

@@ -387,12 +387,13 @@ public class PyDocumentationBuilder {
}
private void addInheritedDocString(@NotNull final PyFunction pyFunction, @Nullable final PyClass pyClass) {
final TypeEvalContext context = TypeEvalContext.userInitiated(pyFunction.getProject(), pyFunction.getContainingFile());
final String methodName = pyFunction.getName();
if (pyClass == null || methodName == null) {
return;
}
final boolean isConstructor = PyNames.INIT.equals(methodName);
Iterable<PyClass> classes = pyClass.getAncestorClasses(null);
Iterable<PyClass> classes = pyClass.getAncestorClasses(context);
if (isConstructor) {
// look at our own class again and maybe inherit class's doc
classes = new ChainIterable<>(pyClass).add(classes);
@@ -401,7 +402,7 @@ public class PyDocumentationBuilder {
PyStringLiteralExpression docstringElement = null;
PyFunction inherited = null;
boolean isFromClass = false;
if (isConstructor) docstringElement = getEffectiveDocStringExpression(pyClass);
if (isConstructor) docstringElement = getEffectiveDocStringExpression(ancestor);
if (docstringElement != null) {
isFromClass = true;
}
@@ -416,7 +417,6 @@ public class PyDocumentationBuilder {
if (inheritedDoc.length() > 1) {
final String ancestorName = ancestor.getName();
final String ancestorQualifiedName = ancestor.getQualifiedName();
final TypeEvalContext context = TypeEvalContext.userInitiated(pyFunction.getProject(), pyFunction.getContainingFile());
final String ancestorLink = pyClass == ancestor
? PyDocumentationLink.toContainingClass(ancestorName)

View File

@@ -0,0 +1 @@
<html><body><div class='definition'><pre><small>class <a href="psi_element://#class#">Sub</a>(<a href="psi_element://#typename#AncestorClassDocstringForConstructor.Base">Base</a>)</small><br><br>def&nbsp;<b>__init__</b>(self:&nbsp;<a href="psi_element://#typename#Sub">Sub</a>)&nbsp;-&gt;&nbsp;None</pre></div><div class='content'>Class&nbsp;docstring.</div><table class='sections'><tr><td valign='top' class='section'><p>Documentation is copied from:</td><td valign='top'><code><a href="psi_element://#typename#AncestorClassDocstringForConstructor.Base">Base</a></code></td></table></body></html>

View File

@@ -0,0 +1,6 @@
class Base:
"""Class docstring."""
class Sub(Base):
def __in<the_ref>it__(self):
pass

View File

@@ -0,0 +1 @@
<html><body><div class='definition'><pre><small>class <a href="psi_element://#class#">Sub</a>(Base)</small><br><br>def&nbsp;<b>__init__</b>(self:&nbsp;Sub)&nbsp;-&gt;&nbsp;None</pre></div><div class='content'>Class&nbsp;docstring.</div></body></html>

View File

@@ -0,0 +1,7 @@
def hidden():
class Base:
"""Class docstring."""
class Sub(Base):
def __in<the_ref>it__(self):
pass

View File

@@ -0,0 +1 @@
<html><body><div class='definition'><pre><small>class <a href="psi_element://#class#">MyClass</a></small><br><br>def&nbsp;<b>__init__</b>(self:&nbsp;<a href="psi_element://#typename#MyClass">MyClass</a>)&nbsp;-&gt;&nbsp;None</pre></div><div class='content'>Class&nbsp;docstring.</div><table class='sections'><tr><td valign='top' class='section'><p>Documentation is copied from:</td><td valign='top'><code><a href="psi_element://#class#">MyClass</a></code></td></table></body></html>

View File

@@ -0,0 +1,4 @@
class MyClass:
"""Class docstring."""
def __in<the_ref>it__(self):
pass

View File

@@ -1,7 +1,7 @@
# copied doc of inherited method
class A:
def foo(self):
"<the_doc>Doc from A.foo."
"Doc from A.foo."
pass
class B(A):

View File

@@ -0,0 +1 @@
<html><body><div class='definition'><pre><small>class <a href="psi_element://#class#">Sub</a>(Base)</small><br><br>def&nbsp;<b>method</b>(self:&nbsp;Sub)&nbsp;-&gt;&nbsp;None</pre></div><div class='content'>Base&nbsp;class&nbsp;docstring.</div></body></html>

View File

@@ -0,0 +1,8 @@
def hidden():
class Base:
def method(self):
"""Base class docstring."""
class Sub(Base):
def met<the_ref>hod(self):
pass

View File

@@ -0,0 +1 @@
<html><body><div class='definition'><pre><small>class <a href="psi_element://#class#">MyClass</a></small><br><br>def&nbsp;<b>__init__</b>(self:&nbsp;MyClass)&nbsp;-&gt;&nbsp;None</pre></div><div class='content'>Class&nbsp;docstring.</div><table class='sections'><tr><td valign='top' class='section'><p>Documentation is copied from:</td><td valign='top'><code><a href="psi_element://#class#">MyClass</a></code></td></table></body></html>

View File

@@ -0,0 +1,5 @@
def hidden():
class MyClass:
"""Class docstring."""
def __in<the_ref>it__(self):
pass

View File

@@ -124,19 +124,27 @@ public class PyQuickDocTest extends LightMarkedTestCase {
}
public void testInheritedMethod() {
Map<String, PsiElement> marks = loadTest();
assertEquals(2, marks.size());
PsiElement docElement = marks.get("<the_doc>").getParent(); // ident -> expr
assertTrue(docElement instanceof PyStringLiteralExpression);
String docText = ((PyStringLiteralExpression)docElement).getStringValue();
assertNotNull(docText);
checkHTMLOnly();
}
PsiElement ref_elt = marks.get("<the_ref>").getParent(); // ident -> expr
final PyDocStringOwner docOwner = (PyDocStringOwner)((PyReferenceExpression)ref_elt).getReference().resolve();
assertNotNull(docOwner);
assertNull(docOwner.getDocStringExpression()); // no direct doc!
public void testInheritedMethodOfInnerClass() {
checkHTMLOnly();
}
checkByHTML(myProvider.generateDoc(docOwner, null));
public void testClassDocstringForConstructor() {
checkHTMLOnly();
}
public void testInnerClassDocstringForConstructor() {
checkHTMLOnly();
}
public void testAncestorClassDocstringForConstructor() {
checkHTMLOnly();
}
public void testAncestorInnerClassDocstringForConstructor() {
checkHTMLOnly();
}
public void testPropNewGetter() {