diff --git a/python/src/com/jetbrains/python/inspections/PyDeprecationInspection.java b/python/src/com/jetbrains/python/inspections/PyDeprecationInspection.java index 614daa62c470..40a10a8a3709 100644 --- a/python/src/com/jetbrains/python/inspections/PyDeprecationInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyDeprecationInspection.java @@ -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); + } + } } } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 5210e32528e7..b0dccdebcace 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -673,6 +673,12 @@ public class PyFunctionImpl extends PyBaseElementImpl 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(); diff --git a/python/testData/MockSdk2.7/Lib/abc.py b/python/testData/MockSdk2.7/Lib/abc.py index ec9311814a02..a2a3475cba64 100644 --- a/python/testData/MockSdk2.7/Lib/abc.py +++ b/python/testData/MockSdk2.7/Lib/abc.py @@ -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 \ No newline at end of file diff --git a/python/testData/deprecation/abcAbstractProperty.py b/python/testData/deprecation/abcDeprecatedAbstracts.py similarity index 70% rename from python/testData/deprecation/abcAbstractProperty.py rename to python/testData/deprecation/abcDeprecatedAbstracts.py index f6511c793149..2c4ea6ce8023 100644 --- a/python/testData/deprecation/abcAbstractProperty.py +++ b/python/testData/deprecation/abcDeprecatedAbstracts.py @@ -31,4 +31,14 @@ import abc as foo class D(metaclass=abc.ABCMeta): @foo.abstractproperty def prop(self): + pass + + +class A: + @abc.abstractclassmethod + def foo(cls): + pass + + @abc.abstractstaticmethod + def bar(): pass \ No newline at end of file diff --git a/python/testData/inspections/PyMethodParametersInspectionAbstractClassAndStaticMethods/test.py b/python/testData/inspections/PyMethodParametersInspectionAbstractClassAndStaticMethods/test.py new file mode 100644 index 000000000000..419e76d99033 --- /dev/null +++ b/python/testData/inspections/PyMethodParametersInspectionAbstractClassAndStaticMethods/test.py @@ -0,0 +1,11 @@ +import abc + + +class A: + @abc.abstractclassmethod + def foo(cls): + pass + + @abc.abstractstaticmethod + def bar(): + pass diff --git a/python/testSrc/com/jetbrains/python/PyDeprecationTest.java b/python/testSrc/com/jetbrains/python/PyDeprecationTest.java index 92bb8ec62183..8a778873ae84 100644 --- a/python/testSrc/com/jetbrains/python/PyDeprecationTest.java +++ b/python/testSrc/com/jetbrains/python/PyDeprecationTest.java @@ -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); } ); diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java index 9b5b76c742d7..5d3d4c2e1ed2 100644 --- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java +++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java @@ -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);