mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
PY-16760, PY-28549, PY-35743, PY-55609, PY-46654: Correct resolve of class attributes in docstrings
Previously in docstrings we incorrectly recognized references in 'Attributes' section only as instance attribute references. It led to false positive 'Unresolved reference' on class attributes references in docstrings and wrong resolve when using class and instance attributes with the same names. Now we use ReferenceType.VARIABLE to identify both class and instance attribute references in 'Attributes' section and then resolve them with priority of instance attributes. Also fixed wrong resolve of attributes references to constructor parameters. GitOrigin-RevId: ea10bfb092472c0ab14b77f06efd93093cfcd684
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ce84c71a5c
commit
cce0ef17d5
@@ -1540,4 +1540,122 @@ public abstract class PyCommonResolveTest extends PyCommonResolveTestCase {
|
||||
assertEquals("ModuleType", target.getContainingClass().getName());
|
||||
assertEquals("types.pyi", target.getContainingFile().getName());
|
||||
}
|
||||
|
||||
// PY-16760
|
||||
public void testGoogleDocstringAttributeNameResolvesToClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.GOOGLE, () -> assertResolvesTo(PyTargetExpression.class, "attr1"));
|
||||
}
|
||||
|
||||
// PY-16760
|
||||
public void testGoogleDocstringAttributeNameResolvesToInstanceAttributeOverClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.GOOGLE, () -> {
|
||||
PyTargetExpression definition = assertResolvesTo(PyTargetExpression.class, "attr1");
|
||||
assertTrue(PyUtil.isInstanceAttribute(definition));
|
||||
});
|
||||
}
|
||||
|
||||
// PY-16760
|
||||
public void testGoogleDocstringAttributeNameResolvesToInstanceAttributeOverInitParameter() {
|
||||
runWithDocStringFormat(DocStringFormat.GOOGLE, () -> assertResolvesTo(PyTargetExpression.class, "attr1"));
|
||||
}
|
||||
|
||||
// PY-16760
|
||||
public void testNumpyDocstringAttributeNameResolvesToClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.NUMPY, () -> assertResolvesTo(PyTargetExpression.class, "attr1"));
|
||||
}
|
||||
|
||||
// PY-28549
|
||||
public void testGoogleDocstringAttributeNameResolvesToDataclassClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.GOOGLE, () -> assertResolvesTo(PyTargetExpression.class, "attr1"));
|
||||
}
|
||||
|
||||
// PY-28549
|
||||
public void testNumpyDocstringAttributeNameResolvesToDataclassClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.NUMPY, () -> assertResolvesTo(PyTargetExpression.class, "attr1"));
|
||||
}
|
||||
|
||||
// PY-28549
|
||||
public void testNumpyDocstringParameterNameResolvesToDataclassClassAttributeWithoutInit() {
|
||||
runWithDocStringFormat(DocStringFormat.NUMPY, () -> assertResolvesTo(PyTargetExpression.class, "attr1"));
|
||||
}
|
||||
|
||||
// PY-28549
|
||||
public void testNumpyDocstringParameterNameUnresolvedWithInit() {
|
||||
runWithDocStringFormat(DocStringFormat.NUMPY, () -> assertUnresolved());
|
||||
}
|
||||
|
||||
// PY-28549
|
||||
public void testNumpyDocstringParameterNameResolvesToDataclassInitParameterOverClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.NUMPY, () -> assertResolvesTo(PyNamedParameter.class, "attr1"));
|
||||
}
|
||||
|
||||
// PY-35743
|
||||
public void testGoogleDocstringAttributeNameResolvesToNamedTupleClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.GOOGLE, () -> assertResolvesTo(PyTargetExpression.class, "attr1"));
|
||||
}
|
||||
|
||||
// PY-35743
|
||||
public void testNumpyDocstringAttributeNameResolvesToNamedTupleClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.NUMPY, () -> assertResolvesTo(PyTargetExpression.class, "attr1"));
|
||||
}
|
||||
|
||||
// PY-55609
|
||||
public void testRestDocstringVarNameResolvesToInstanceAttributeOverInitParameter() {
|
||||
runWithDocStringFormat(DocStringFormat.REST, () -> {
|
||||
PyTargetExpression definition = assertResolvesTo(PyTargetExpression.class, "var1");
|
||||
assertTrue(PyUtil.isInstanceAttribute(definition));
|
||||
});
|
||||
}
|
||||
|
||||
// PY-55609
|
||||
public void testRestDocstringVarNameResolvesToInstanceAttributeOverClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.REST, () -> {
|
||||
PyTargetExpression definition = assertResolvesTo(PyTargetExpression.class, "var1");
|
||||
assertTrue(PyUtil.isInstanceAttribute(definition));
|
||||
});
|
||||
}
|
||||
|
||||
// PY-55609
|
||||
public void testRestDocstringIvarNameResolvesToInstanceAttributeOverInitParameter() {
|
||||
runWithDocStringFormat(DocStringFormat.REST, () -> {
|
||||
PyTargetExpression definition = assertResolvesTo(PyTargetExpression.class, "var1");
|
||||
assertTrue(PyUtil.isInstanceAttribute(definition));
|
||||
});
|
||||
}
|
||||
|
||||
// PY-55609
|
||||
public void testRestDocstringCvarNameResolvesToClassAttributeOverInitParameter() {
|
||||
runWithDocStringFormat(DocStringFormat.REST, () -> {
|
||||
PyTargetExpression definition = assertResolvesTo(PyTargetExpression.class, "var1");
|
||||
assertTrue(PyUtil.isClassAttribute(definition));
|
||||
});
|
||||
}
|
||||
|
||||
// PY-55609
|
||||
public void testRestDocstringCvarNameResolvesToClassAttributeOverInstanceAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.REST, () -> {
|
||||
PyTargetExpression definition = assertResolvesTo(PyTargetExpression.class, "var1");
|
||||
assertTrue(PyUtil.isClassAttribute(definition));
|
||||
});
|
||||
}
|
||||
|
||||
// PY-55609
|
||||
public void testRestDocstringVarNameResolvesToClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.REST, () -> assertResolvesTo(PyTargetExpression.class, "var1"));
|
||||
}
|
||||
|
||||
// PY-55609
|
||||
public void testRestDocstringTypeOwnerNameResolvesToInitParameterOverClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.REST, () -> assertResolvesTo(PyNamedParameter.class, "p"));
|
||||
}
|
||||
|
||||
// PY-55609
|
||||
public void testRestDocstringTypeOwnerNameResolvesToInitParameterOverInstanceAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.REST, () -> assertResolvesTo(PyNamedParameter.class, "p"));
|
||||
}
|
||||
|
||||
// PY-46654
|
||||
public void testRestDocstringIvarNameResolvesToDataClassAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.REST, () -> assertResolvesTo(PyTargetExpression.class, "var1"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,26 +38,23 @@ public class DocStringParameterReference extends PsiReferenceBase<PyStringLitera
|
||||
return resolveParameter((PyFunction)owner);
|
||||
}
|
||||
if (owner instanceof PyClass) {
|
||||
final PyFunction init = ((PyClass)owner).findMethodByName(PyNames.INIT, false, null);
|
||||
if (init != null) {
|
||||
PyElement element = resolveParameter(init);
|
||||
if (element == null && (myType.equals(ReferenceType.CLASS_VARIABLE) || myType.equals(ReferenceType.PARAMETER_TYPE))) {
|
||||
element = resolveClassVariable((PyClass)owner);
|
||||
}
|
||||
if (element == null && (myType.equals(ReferenceType.INSTANCE_VARIABLE) || myType.equals(ReferenceType.PARAMETER_TYPE))) {
|
||||
element = resolveInstanceVariable((PyClass)owner);
|
||||
}
|
||||
return element;
|
||||
PyClass pyClass = (PyClass)owner;
|
||||
final PyFunction init = pyClass.findMethodByName(PyNames.INIT, false, null);
|
||||
if (myType == ReferenceType.PARAMETER) {
|
||||
return init != null ? resolveParameter(init) : resolveClassVariable(pyClass);
|
||||
}
|
||||
else {
|
||||
PyElement element = null;
|
||||
if (myType.equals(ReferenceType.CLASS_VARIABLE) || myType.equals(ReferenceType.PARAMETER_TYPE)) {
|
||||
element = resolveClassVariable((PyClass)owner);
|
||||
if (myType == ReferenceType.INSTANCE_VARIABLE || myType == ReferenceType.VARIABLE || myType == ReferenceType.PARAMETER_TYPE) {
|
||||
if (myType == ReferenceType.PARAMETER_TYPE && init != null) {
|
||||
PyNamedParameter parameter = resolveParameter(init);
|
||||
if (parameter != null) {
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
if (element == null && (myType.equals(ReferenceType.INSTANCE_VARIABLE) || myType.equals(ReferenceType.PARAMETER_TYPE))) {
|
||||
element = resolveInstanceVariable((PyClass)owner);
|
||||
}
|
||||
return element;
|
||||
PyElement instanceAttr = resolveInstanceVariable(pyClass);
|
||||
return instanceAttr != null ? instanceAttr : resolveClassVariable(pyClass);
|
||||
}
|
||||
if (myType == ReferenceType.CLASS_VARIABLE) {
|
||||
return resolveClassVariable(pyClass);
|
||||
}
|
||||
}
|
||||
if (owner instanceof PyFile && myType == ReferenceType.GLOBAL_VARIABLE) {
|
||||
|
||||
@@ -87,8 +87,8 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
|
||||
final SectionBasedDocString sectioned = (SectionBasedDocString)docString;
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getParameterFields(), ReferenceType.PARAMETER));
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getKeywordArgumentFields(), ReferenceType.KEYWORD));
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getAttributeFields(),
|
||||
PyUtil.isTopLevel(element) ? ReferenceType.GLOBAL_VARIABLE : ReferenceType.INSTANCE_VARIABLE));
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getAttributeFields(),
|
||||
PyUtil.isTopLevel(element) ? ReferenceType.GLOBAL_VARIABLE : ReferenceType.VARIABLE));
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getReturnFields(), null));
|
||||
}
|
||||
return result.toArray(PsiReference.EMPTY_ARRAY);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Attributes:
|
||||
at<caret>tr1: attr1 description
|
||||
"""
|
||||
attr1 = 1
|
||||
@@ -0,0 +1,7 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Attributes:
|
||||
bar: attr1 description
|
||||
"""
|
||||
bar = 1
|
||||
@@ -0,0 +1,10 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Attributes:
|
||||
at<caret>tr1: attr1 description
|
||||
"""
|
||||
attr1 = 1
|
||||
@@ -0,0 +1,10 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Attributes:
|
||||
bar: attr1 description
|
||||
"""
|
||||
bar = 1
|
||||
@@ -0,0 +1,12 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""
|
||||
Class description
|
||||
|
||||
Parameters:
|
||||
at<caret>tr1: attribute description
|
||||
|
||||
"""
|
||||
attr1 = 3
|
||||
@@ -0,0 +1,12 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""
|
||||
Class description
|
||||
|
||||
Parameters:
|
||||
bar: attribute description
|
||||
|
||||
"""
|
||||
bar = 3
|
||||
@@ -0,0 +1,15 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""
|
||||
Class description
|
||||
|
||||
Parameters:
|
||||
at<caret>tr1: attribute description
|
||||
|
||||
"""
|
||||
attr1 = 3
|
||||
|
||||
def __init__(self, attr1):
|
||||
pass
|
||||
@@ -0,0 +1,15 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""
|
||||
Class description
|
||||
|
||||
Parameters:
|
||||
bar: attribute description
|
||||
|
||||
"""
|
||||
attr1 = 3
|
||||
|
||||
def __init__(self, bar):
|
||||
pass
|
||||
@@ -0,0 +1,8 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Attributes:
|
||||
attr1: attr1 description
|
||||
<ref>
|
||||
"""
|
||||
attr1 = 1
|
||||
@@ -0,0 +1,11 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Attributes:
|
||||
attr1: attr1 description
|
||||
<ref>
|
||||
"""
|
||||
attr1 = 1
|
||||
@@ -0,0 +1,11 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Attributes:
|
||||
attr1: attr1 description
|
||||
<ref>
|
||||
"""
|
||||
attr1 = 1
|
||||
|
||||
def __init__(self):
|
||||
self.attr1 = 0
|
||||
@@ -0,0 +1,10 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Attributes:
|
||||
attr1: attr1 description
|
||||
<ref>
|
||||
"""
|
||||
|
||||
def __init__(self, attr1):
|
||||
self.attr1 = attr1
|
||||
@@ -0,0 +1,10 @@
|
||||
from typing import NamedTuple
|
||||
|
||||
class SomeObject(NamedTuple):
|
||||
"""Class description
|
||||
|
||||
Attributes:
|
||||
attr1: attr1 description
|
||||
<ref>
|
||||
"""
|
||||
attr1 = 1
|
||||
@@ -0,0 +1,10 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Attributes
|
||||
----------
|
||||
attr1:
|
||||
<ref>
|
||||
attr1 description
|
||||
"""
|
||||
attr1 = 1
|
||||
@@ -0,0 +1,13 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Attributes
|
||||
----------
|
||||
attr1:
|
||||
<ref>
|
||||
attr1 description
|
||||
"""
|
||||
attr1 = 1
|
||||
@@ -0,0 +1,12 @@
|
||||
from typing import NamedTuple
|
||||
|
||||
class SomeObject(NamedTuple):
|
||||
"""Class description
|
||||
|
||||
Attributes
|
||||
---------
|
||||
attr1:
|
||||
<ref>
|
||||
attr1 description
|
||||
"""
|
||||
attr1 = 1
|
||||
@@ -0,0 +1,13 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Parameters
|
||||
----------
|
||||
attr1:
|
||||
<ref>
|
||||
attr1 description
|
||||
"""
|
||||
attr1 = 1
|
||||
@@ -0,0 +1,16 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Parameters
|
||||
----------
|
||||
attr1:
|
||||
<ref>
|
||||
attr1 description
|
||||
"""
|
||||
attr1 = 1
|
||||
|
||||
def __init__(self, attr1):
|
||||
pass
|
||||
@@ -0,0 +1,16 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
Parameters
|
||||
----------
|
||||
attr1:
|
||||
<ref>
|
||||
attr1 description
|
||||
"""
|
||||
attr1 = 1
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -0,0 +1,11 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
:cvar var1: var1 description
|
||||
<ref>
|
||||
"""
|
||||
|
||||
var1 = 1
|
||||
|
||||
def __init__(self, var1):
|
||||
pass
|
||||
@@ -0,0 +1,11 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
:cvar var1: var1 description
|
||||
<ref>
|
||||
"""
|
||||
|
||||
var1 = 1
|
||||
|
||||
def __init__(self):
|
||||
self.var1 = 0
|
||||
@@ -0,0 +1,9 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class MyClass:
|
||||
"""
|
||||
:ivar var1: description
|
||||
<ref>
|
||||
"""
|
||||
var1 = 0
|
||||
@@ -0,0 +1,9 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
:ivar var1: var1 description
|
||||
<ref>
|
||||
"""
|
||||
|
||||
def __init__(self, var1):
|
||||
self.var1 = var1
|
||||
@@ -0,0 +1,10 @@
|
||||
class MyClass:
|
||||
"""
|
||||
:param p: description
|
||||
:type p: int
|
||||
<ref>
|
||||
"""
|
||||
p = 0
|
||||
|
||||
def __init__(self, p):
|
||||
pass
|
||||
@@ -0,0 +1,9 @@
|
||||
class MyClass:
|
||||
"""
|
||||
:param p: description
|
||||
:type p: int
|
||||
<ref>
|
||||
"""
|
||||
|
||||
def __init__(self, p):
|
||||
self.p = p
|
||||
@@ -0,0 +1,8 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
:var var1: var1 description
|
||||
<ref>
|
||||
"""
|
||||
|
||||
var1 = 1
|
||||
@@ -0,0 +1,11 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
:var var1: var1 description
|
||||
<ref>
|
||||
"""
|
||||
|
||||
var1 = 1
|
||||
|
||||
def __init__(self):
|
||||
self.var1 = 0
|
||||
@@ -0,0 +1,9 @@
|
||||
class MyClass:
|
||||
"""Class description
|
||||
|
||||
:var var1: var1 description
|
||||
<ref>
|
||||
"""
|
||||
|
||||
def __init__(self, var1):
|
||||
self.var1 = var1
|
||||
@@ -224,6 +224,26 @@ public class PyRenameTest extends PyTestCase {
|
||||
renameWithDocStringFormat(DocStringFormat.NUMPY, "bar");
|
||||
}
|
||||
|
||||
// PY-16760
|
||||
public void testGoogleDocstringAttributeRenamesWithClassAttribute() {
|
||||
renameWithDocStringFormat(DocStringFormat.GOOGLE, "bar");
|
||||
}
|
||||
|
||||
// PY-28549
|
||||
public void testGoogleDocstringAttributeRenamesWithDataclassClassAttribute() {
|
||||
renameWithDocStringFormat(DocStringFormat.GOOGLE, "bar");
|
||||
}
|
||||
|
||||
// PY-28549
|
||||
public void testGoogleDocstringDataClassParameterRenamesWithClassAttribute() {
|
||||
renameWithDocStringFormat(DocStringFormat.GOOGLE, "bar");
|
||||
}
|
||||
|
||||
// PY-28549
|
||||
public void testGoogleDocstringDataClassParameterRenamesWithInitParameterOverClassAttribute() {
|
||||
renameWithDocStringFormat(DocStringFormat.GOOGLE, "bar");
|
||||
}
|
||||
|
||||
// PY-2748
|
||||
public void testFormatStringDictLiteral() {
|
||||
doUnsupportedOperationTest();
|
||||
|
||||
Reference in New Issue
Block a user