From a8fcaed8ff27a9af0ba5663fbde95a08db595e18 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Mon, 5 Sep 2016 19:58:12 +0300 Subject: [PATCH] EA-87708 Fixed: NPE: PyFunctionImpl.getWrappersFromStub PY-19412 Fixed: Class method support becomes broken if method attribute is used Check all next siblings stubs of PyTargetExpressionStub type in function modifier calculating and honour all nullable values --- .../python/psi/impl/PyFunctionImpl.java | 56 ++++++++++++------- .../a.py | 7 +++ .../b.py | 3 + .../PyArgumentListInspectionTest.java | 10 ++++ 4 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 python/testData/inspections/PyArgumentListInspection/ReassignedViaClassMethodInAnotherModule/a.py create mode 100644 python/testData/inspections/PyArgumentListInspection/ReassignedViaClassMethodInAnotherModule/b.py diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index a255c9e08e07..0df11125a6c4 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -34,6 +34,7 @@ import com.intellij.psi.util.*; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.PlatformIcons; +import com.intellij.util.containers.JBIterable; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; @@ -636,15 +637,18 @@ public class PyFunctionImpl extends PyBaseElementImpl implements else if (PyNames.STATICMETHOD.equals(deconame)) { return STATICMETHOD; } + // implicit staticmethod __new__ final PyClass cls = getContainingClass(); if (cls != null && PyNames.NEW.equals(getName()) && cls.isNewStyleClass(null)) { return STATICMETHOD; } - // - if (getStub() != null) { - return getWrappersFromStub(); + + final PyFunctionStub stub = getStub(); + if (stub != null) { + return getModifierFromStub(stub); } + final String funcName = getName(); if (funcName != null) { PyAssignmentStatement currentAssignment = PsiTreeUtil.getNextSiblingOfType(this, PyAssignmentStatement.class); @@ -671,6 +675,7 @@ public class PyFunctionImpl extends PyBaseElementImpl implements currentAssignment = PsiTreeUtil.getNextSiblingOfType(currentAssignment, PyAssignmentStatement.class); } } + return null; } @@ -696,25 +701,36 @@ public class PyFunctionImpl extends PyBaseElementImpl implements } @Nullable - private Modifier getWrappersFromStub() { - final StubElement parentStub = getStub().getParentStub(); - final List childrenStubs = parentStub.getChildrenStubs(); - int index = childrenStubs.indexOf(getStub()); - if (index >= 0 && index < childrenStubs.size() - 1) { - StubElement nextStub = (StubElement)childrenStubs.get(index + 1); - if (nextStub instanceof PyTargetExpressionStub) { - final PyTargetExpressionStub targetExpressionStub = (PyTargetExpressionStub)nextStub; - if (targetExpressionStub.getInitializerType() == PyTargetExpressionStub.InitializerType.CallExpression) { - final QualifiedName qualifiedName = targetExpressionStub.getInitializer(); - if (QualifiedName.fromComponents(PyNames.CLASSMETHOD).equals(qualifiedName)) { - return CLASSMETHOD; + private static Modifier getModifierFromStub(@NotNull PyFunctionStub stub) { + final Optional> siblingsStubsOptional = Optional + .of(stub) + .map(StubElement::getParentStub) + .map(StubElement::getChildrenStubs); + + if (siblingsStubsOptional.isPresent()) { + return JBIterable + .from(siblingsStubsOptional.get()) + .skipWhile(siblingStub -> !stub.equals(siblingStub)) + .transform(nextSiblingStub -> as(nextSiblingStub, PyTargetExpressionStub.class)) + .filter(Objects::nonNull) + .filter(nextSiblingStub -> nextSiblingStub.getInitializerType() == PyTargetExpressionStub.InitializerType.CallExpression) + .transform(PyTargetExpressionStub::getInitializer) + .transform( + initializerName -> { + if (initializerName.matches(PyNames.CLASSMETHOD)) { + return CLASSMETHOD; + } + else if (initializerName.matches(PyNames.STATICMETHOD)) { + return STATICMETHOD; + } + else { + return null; + } } - if (QualifiedName.fromComponents(PyNames.STATICMETHOD).equals(qualifiedName)) { - return STATICMETHOD; - } - } - } + ) + .find(Objects::nonNull); } + return null; } diff --git a/python/testData/inspections/PyArgumentListInspection/ReassignedViaClassMethodInAnotherModule/a.py b/python/testData/inspections/PyArgumentListInspection/ReassignedViaClassMethodInAnotherModule/a.py new file mode 100644 index 000000000000..f3363a3cc7c7 --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/ReassignedViaClassMethodInAnotherModule/a.py @@ -0,0 +1,7 @@ +class Spam(object): + + def spam(cls): + pass + + eggs = False + spam = classmethod(spam) \ No newline at end of file diff --git a/python/testData/inspections/PyArgumentListInspection/ReassignedViaClassMethodInAnotherModule/b.py b/python/testData/inspections/PyArgumentListInspection/ReassignedViaClassMethodInAnotherModule/b.py new file mode 100644 index 000000000000..5286e529f94d --- /dev/null +++ b/python/testData/inspections/PyArgumentListInspection/ReassignedViaClassMethodInAnotherModule/b.py @@ -0,0 +1,3 @@ +from a import Spam + +Spam.spam() \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java index b3c7f412edcf..18f4cd651350 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyArgumentListInspectionTest.java @@ -95,6 +95,16 @@ public class PyArgumentListInspectionTest extends PyTestCase { doTest(); } + // PY-19412 + public void testReassignedViaClassMethodInAnotherModule() { + final String folderPath = "inspections/PyArgumentListInspection/ReassignedViaClassMethodInAnotherModule/"; + + myFixture.copyDirectoryToProject(folderPath, ""); + myFixture.configureFromTempProjectFile("b.py"); + myFixture.enableInspections(PyArgumentListInspection.class); + myFixture.checkHighlighting(true, false, false); + } + // PY-2294 public void testTuples() { doTest();