Support abc.abstractstaticmethod and abc.abstractclassmethod (PY-14896)

Modifier inference for functions decorated with these decorators was fixed.
Usages of these decorators are highlighted as deprecated.
This commit is contained in:
Semyon Proshev
2018-02-09 17:22:50 +03:00
parent 93a300f3e6
commit c536e920b6
7 changed files with 72 additions and 20 deletions
@@ -22,15 +22,12 @@ import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.QualifiedName;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.PyKnownDecoratorUtil.KnownDecorator;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
/**
* @author yole
*/
@@ -87,23 +84,34 @@ public class PyDeprecationInspection extends PyInspection {
final PyDecoratorList decoratorList = node.getDecoratorList();
if (!LanguageLevel.forElement(node).isPython2() && decoratorList != null) {
Arrays
.stream(decoratorList.getDecorators())
.filter(
decorator -> PyKnownDecoratorUtil.asKnownDecorators(decorator, myTypeEvalContext).contains(KnownDecorator.ABC_ABSTRACTPROPERTY)
)
.forEach(
decorator -> {
final QualifiedName abcAbsPropertyQName = KnownDecorator.ABC_ABSTRACTPROPERTY.getQualifiedName();
final QualifiedName propertyQName = KnownDecorator.PROPERTY.getQualifiedName();
final QualifiedName abcAbsMethodQName = KnownDecorator.ABC_ABSTRACTMETHOD.getQualifiedName();
for (PyDecorator decorator : decoratorList.getDecorators()) {
for (KnownDecorator knownDecorator : PyKnownDecoratorUtil.asKnownDecorators(decorator, myTypeEvalContext)) {
final KnownDecorator deprecated;
final KnownDecorator builtin;
final String message = "'" + abcAbsPropertyQName + "' is deprecated since Python 3.3. " +
"Use '" + propertyQName + "' with '" + abcAbsMethodQName + "' instead.";
registerProblem(decorator, message, ProblemHighlightType.LIKE_DEPRECATED);
if (knownDecorator == KnownDecorator.ABC_ABSTRACTPROPERTY) {
deprecated = KnownDecorator.ABC_ABSTRACTPROPERTY;
builtin = KnownDecorator.PROPERTY;
}
);
else if (knownDecorator == KnownDecorator.ABC_ABSTRACTCLASSMETHOD) {
deprecated = KnownDecorator.ABC_ABSTRACTCLASSMETHOD;
builtin = KnownDecorator.CLASSMETHOD;
}
else if (knownDecorator == KnownDecorator.ABC_ABSTRACTSTATICMETHOD) {
deprecated = KnownDecorator.ABC_ABSTRACTSTATICMETHOD;
builtin = KnownDecorator.STATICMETHOD;
}
else {
continue;
}
final KnownDecorator abcAbsMethod = KnownDecorator.ABC_ABSTRACTMETHOD;
final String message = "'" + deprecated.getQualifiedName() + "' is deprecated since Python 3.3. " +
"Use '" + builtin.getQualifiedName() + "' with '" + abcAbsMethod.getQualifiedName() + "' instead.";
registerProblem(decorator, message, ProblemHighlightType.LIKE_DEPRECATED);
}
}
}
}
}
@@ -673,6 +673,12 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
if (PyNames.CLASS_GETITEM.equals(funcName) && level.isAtLeast(LanguageLevel.PYTHON37)) {
return CLASSMETHOD;
}
final TypeEvalContext context = TypeEvalContext.codeInsightFallback(getProject());
for (PyKnownDecoratorUtil.KnownDecorator knownDecorator : PyKnownDecoratorUtil.getKnownDecorators(this, context)) {
if (knownDecorator == PyKnownDecoratorUtil.KnownDecorator.ABC_ABSTRACTCLASSMETHOD) return CLASSMETHOD;
if (knownDecorator == PyKnownDecoratorUtil.KnownDecorator.ABC_ABSTRACTSTATICMETHOD) return STATICMETHOD;
}
}
final PyFunctionStub stub = getStub();
+12
View File
@@ -9,4 +9,16 @@ def abstractmethod(foo):
def abstractproperty(foo):
pass
# Important:
# classes below are not presented in Python 2
# they were added just for PyDeprecationTest#testAbcDeprecatedAbstracts
# to not create separate test case for them
class abstractstaticmethod(staticmethod):
pass
class abstractclassmethod(classmethod):
pass
@@ -31,4 +31,14 @@ 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
class A:
<warning descr="'abc.abstractclassmethod' is deprecated since Python 3.3. Use 'classmethod' with 'abc.abstractmethod' instead.">@abc.abstractclassmethod</warning>
def foo(cls):
pass
<warning descr="'abc.abstractstaticmethod' is deprecated since Python 3.3. Use 'staticmethod' with 'abc.abstractmethod' instead.">@abc.abstractstaticmethod</warning>
def bar():
pass
@@ -0,0 +1,11 @@
import abc
class A:
@abc.abstractclassmethod
def foo(cls):
pass
@abc.abstractstaticmethod
def bar():
pass
@@ -78,12 +78,12 @@ public class PyDeprecationTest extends PyTestCase {
myFixture.checkHighlighting(true, false, false);
}
public void testAbsAbstractProperty() {
public void testAbcDeprecatedAbstracts() {
runWithLanguageLevel(
LanguageLevel.PYTHON34,
() -> {
myFixture.enableInspections(PyDeprecationInspection.class);
myFixture.configureByFile("deprecation/abcAbstractProperty.py");
myFixture.configureByFile("deprecation/abcDeprecatedAbstracts.py");
myFixture.checkHighlighting(true, false, false);
}
);
@@ -58,6 +58,11 @@ public class PythonInspectionsTest extends PyTestCase {
doHighlightingTest(PyMethodParametersInspection.class, LanguageLevel.PYTHON37);
}
// PY-14896
public void testPyMethodParametersInspectionAbstractClassAndStaticMethods() {
doHighlightingTest(PyMethodParametersInspection.class, LanguageLevel.PYTHON34);
}
public void testPyNestedDecoratorsInspection() {
LocalInspectionTool inspection = new PyNestedDecoratorsInspection();
doTest(getTestName(false), inspection);