mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-20322 Fixed: False positive warnings about abstract property
Use PyKnownDecoratorUtil to check if the specified decorator is one of the known decorators
This commit is contained in:
committed by
Semyon Proshev
parent
6d223ec43c
commit
c55d28bc8b
@@ -89,7 +89,7 @@ public class PyDeprecationInspection extends PyInspection {
|
||||
if (LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON33) && decoratorList != null) {
|
||||
Arrays
|
||||
.stream(decoratorList.getDecorators())
|
||||
.filter(decorator -> KnownDecorator.ABC_ABSTRACTPROPERTY.getQualifiedName().equals(decorator.getQualifiedName()))
|
||||
.filter(decorator -> PyKnownDecoratorUtil.asKnownDecorator(decorator, myTypeEvalContext) == KnownDecorator.ABC_ABSTRACTPROPERTY)
|
||||
.forEach(
|
||||
decorator -> {
|
||||
final QualifiedName abcAbsPropertyQName = KnownDecorator.ABC_ABSTRACTPROPERTY.getQualifiedName();
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.psi.util.QualifiedName;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -123,34 +124,41 @@ public class PyKnownDecoratorUtil {
|
||||
if (decoratorList == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<KnownDecorator> result = new ArrayList<>();
|
||||
final boolean allowResolve = context.maySwitchToAST((PsiElement)element);
|
||||
for (PyDecorator decorator : decoratorList.getDecorators()) {
|
||||
final QualifiedName qualifiedName = decorator.getQualifiedName();
|
||||
if (qualifiedName == null) {
|
||||
continue;
|
||||
|
||||
return StreamEx
|
||||
.of(decoratorList.getDecorators())
|
||||
.map(decorator -> asKnownDecorator(decorator, context))
|
||||
.nonNull()
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static KnownDecorator asKnownDecorator(@NotNull PyDecorator decorator, @NotNull TypeEvalContext context) {
|
||||
final QualifiedName qualifiedName = decorator.getQualifiedName();
|
||||
if (qualifiedName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (context.maySwitchToAST(decorator)) {
|
||||
PyQualifiedNameOwner resolved = as(resolveDecorator(decorator), PyQualifiedNameOwner.class);
|
||||
if (resolved instanceof PyFunction && PyNames.INIT.equals(resolved.getName())) {
|
||||
resolved = ((PyFunction)resolved).getContainingClass();
|
||||
}
|
||||
|
||||
final KnownDecorator knownDecorator = ourByShortName.get(qualifiedName.getLastComponent());
|
||||
if (knownDecorator != null) {
|
||||
if (allowResolve) {
|
||||
PyQualifiedNameOwner resolved = as(resolveDecorator(decorator), PyQualifiedNameOwner.class);
|
||||
if (resolved instanceof PyFunction && PyNames.INIT.equals(resolved.getName())) {
|
||||
resolved = ((PyFunction)resolved).getContainingClass();
|
||||
}
|
||||
if (resolved != null && resolved.getQualifiedName() != null) {
|
||||
final QualifiedName resolvedName = QualifiedName.fromDottedString(resolved.getQualifiedName());
|
||||
if (resolvedName.equals(knownDecorator.getQualifiedName())) {
|
||||
result.add(knownDecorator);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.add(knownDecorator);
|
||||
if (resolved != null && resolved.getQualifiedName() != null) {
|
||||
final QualifiedName resolvedName = QualifiedName.fromDottedString(resolved.getQualifiedName());
|
||||
final KnownDecorator knownDecorator = ourByShortName.get(resolvedName.getLastComponent());
|
||||
|
||||
if (knownDecorator != null && resolvedName.equals(knownDecorator.getQualifiedName())) {
|
||||
return knownDecorator;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
else {
|
||||
return ourByShortName.get(qualifiedName.getLastComponent());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -765,7 +765,9 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
}
|
||||
}
|
||||
if (PyNames.PROPERTY.equals(decoName) ||
|
||||
qname.equals(PyKnownDecoratorUtil.KnownDecorator.ABC_ABSTRACTPROPERTY.getQualifiedName())) {
|
||||
ArrayUtil.contains(PyKnownDecoratorUtil.asKnownDecorator(deco, TypeEvalContext.codeInsightFallback(getProject())),
|
||||
PyKnownDecoratorUtil.KnownDecorator.ABC_ABSTRACTPROPERTY,
|
||||
PyKnownDecoratorUtil.KnownDecorator.PROPERTY)) {
|
||||
getter = new Maybe<>(method);
|
||||
}
|
||||
else if (useAdvancedSyntax && qname.matches(decoratorName, PyNames.GETTER)) {
|
||||
|
||||
@@ -5,4 +5,8 @@ class ABCMeta:
|
||||
|
||||
|
||||
def abstractmethod(foo):
|
||||
pass
|
||||
|
||||
|
||||
def abstractproperty(foo):
|
||||
pass
|
||||
@@ -3,5 +3,32 @@ import abc
|
||||
|
||||
class A(metaclass=abc.ABCMeta):
|
||||
<warning descr="'abc.abstractproperty' is deprecated since Python 3.3. Use 'property' with 'abc.abstractmethod' instead.">@abc.abstractproperty</warning>
|
||||
def prop(self):
|
||||
pass
|
||||
|
||||
|
||||
from abc import abstractproperty
|
||||
|
||||
|
||||
class B(metaclass=abc.ABCMeta):
|
||||
<warning descr="'abc.abstractproperty' is deprecated since Python 3.3. Use 'property' with 'abc.abstractmethod' instead.">@abstractproperty</warning>
|
||||
def prop(self):
|
||||
pass
|
||||
|
||||
|
||||
from abc import abstractproperty as ap
|
||||
|
||||
|
||||
class C(metaclass=abc.ABCMeta):
|
||||
<warning descr="'abc.abstractproperty' is deprecated since Python 3.3. Use 'property' with 'abc.abstractmethod' instead.">@ap</warning>
|
||||
def prop(self):
|
||||
pass
|
||||
|
||||
|
||||
import abc as foo
|
||||
|
||||
|
||||
class D(metaclass=abc.ABCMeta):
|
||||
<warning descr="'abc.abstractproperty' is deprecated since Python 3.3. Use 'property' with 'abc.abstractmethod' instead.">@foo.abstractproperty</warning>
|
||||
def prop(self):
|
||||
pass
|
||||
@@ -0,0 +1,15 @@
|
||||
from abc import ABCMeta, abstractproperty
|
||||
|
||||
class Upper(object):
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@abstractproperty
|
||||
def prop(self):
|
||||
pass
|
||||
|
||||
@prop.setter
|
||||
def prop(self, prop):
|
||||
pass
|
||||
|
||||
def foo(self):
|
||||
print self.prop
|
||||
@@ -1,3 +1,6 @@
|
||||
import abc
|
||||
|
||||
|
||||
class A(object):
|
||||
def __init__(self):
|
||||
self._x = 1
|
||||
@@ -65,7 +68,7 @@ class A(object):
|
||||
foo2 = property(get_foo2)
|
||||
|
||||
@property
|
||||
@abstractproperty
|
||||
@abc.abstractproperty
|
||||
def abstract_property(self):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import abc
|
||||
|
||||
|
||||
class A:
|
||||
@property
|
||||
def <warning descr="Getter should return or yield something">normal_property</warning>(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractproperty
|
||||
@abc.abstractproperty
|
||||
def abstract_property1(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
@abc.abstractmethod
|
||||
def abstract_property2(self):
|
||||
pass
|
||||
|
||||
@@ -1472,6 +1472,27 @@ public class PyTypeTest extends PyTestCase {
|
||||
"expr = D().foo");
|
||||
}
|
||||
|
||||
public void testAbsAbstractPropertyWithFrom() {
|
||||
doTest("str",
|
||||
"from abc import abstractproperty\n" +
|
||||
"class D:\n" +
|
||||
" @abstractproperty\n" +
|
||||
" def foo(self):\n" +
|
||||
" return 'foo'\n" +
|
||||
"expr = D().foo");
|
||||
}
|
||||
|
||||
// TODO: enable this test when properties will be calculated with TypeEvalContext
|
||||
public void ignoredTestAbsAbstractPropertyWithAs() {
|
||||
doTest("str",
|
||||
"from abc import abstractproperty as ap\n" +
|
||||
"class D:\n" +
|
||||
" @ap\n" +
|
||||
" def foo(self):\n" +
|
||||
" return 'foo'\n" +
|
||||
"expr = D().foo");
|
||||
}
|
||||
|
||||
// PY-20409
|
||||
public void testGetFromDictWithDefaultNoneValue() {
|
||||
doTest("Any",
|
||||
|
||||
@@ -31,6 +31,11 @@ public class PyPropertyAccessInspectionTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-20322
|
||||
public void testAbcAbstractProperty() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
setLanguageLevel(LanguageLevel.PYTHON26);
|
||||
myFixture.configureByFile("inspections/PyPropertyAccessInspection/" + getTestName(true) + ".py");
|
||||
|
||||
Reference in New Issue
Block a user