diff --git a/python/python-psi-impl/resources/META-INF/PythonPsiImpl.xml b/python/python-psi-impl/resources/META-INF/PythonPsiImpl.xml index 2abed09efa7c..f509d1cb2d4f 100644 --- a/python/python-psi-impl/resources/META-INF/PythonPsiImpl.xml +++ b/python/python-psi-impl/resources/META-INF/PythonPsiImpl.xml @@ -125,7 +125,6 @@ - diff --git a/python/python-psi-impl/resources/inspectionDescriptions/PyCallByClassInspection.html b/python/python-psi-impl/resources/inspectionDescriptions/PyCallByClassInspection.html deleted file mode 100644 index b26d096f33f9..000000000000 --- a/python/python-psi-impl/resources/inspectionDescriptions/PyCallByClassInspection.html +++ /dev/null @@ -1,12 +0,0 @@ - - -This inspection checks for calls of a method by class while passing an instance of a different class -as self parameter: -
-  foo = Foo()
-  Bar.baz(foo, *more)
-
-Sometimes this may be intentional and correct. -But when unintentional, this leads to subtle bugs. - - diff --git a/python/python-psi-impl/resources/messages/PyPsiBundle.properties b/python/python-psi-impl/resources/messages/PyPsiBundle.properties index 7b767a61cbc4..a8745ed2cefe 100644 --- a/python/python-psi-impl/resources/messages/PyPsiBundle.properties +++ b/python/python-psi-impl/resources/messages/PyPsiBundle.properties @@ -808,11 +808,6 @@ INSP.async.call=Coroutine is not awaited INSP.NAME.attribute.outside.init=Instance attribute defined outside __init__ INSP.attribute.outside.init=Instance attribute {0} defined outside __init__ -# PyCallByClassInspection -INSP.NAME.different.class.call=Calling a method by class using an instance of a different class -INSP.different.class.call.instance.of.class.expected.not.class.itself=An instance of {0} expected, not the class itself -INSP.different.class.call.passing.class.instead.class=Passing {0} instead of {1}. Is this intentional? - # PyMissingOrEmptyDocstringInspection INSP.NAME.missing.or.empty.docstring=Missing or empty docstring INSP.no.docstring=Missing docstring diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyCallByClassInspection.java b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyCallByClassInspection.java deleted file mode 100644 index db2efa129ab9..000000000000 --- a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyCallByClassInspection.java +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2000-2017 JetBrains s.r.o. -// Use of this source code is governed by the Apache 2.0 license that can be -// found in the LICENSE file. -package com.jetbrains.python.inspections; - -import com.intellij.codeHighlighting.HighlightDisplayLevel; -import com.intellij.codeInspection.LocalInspectionToolSession; -import com.intellij.codeInspection.ProblemsHolder; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.containers.ContainerUtil; -import com.jetbrains.python.PyPsiBundle; -import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.types.PyCallableParameter; -import com.jetbrains.python.psi.types.PyCallableType; -import com.jetbrains.python.psi.types.PyClassType; -import com.jetbrains.python.psi.types.PyType; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; - -import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD; -import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD; - -/** - * Checks for for calls like {@code X.method(y,...)}, where y is not an instance of X. - *
- * Not marked are cases of inheritance calls in old-style classes, like:
- * class B(A):
- *   def foo(self):
- *     A.foo(self)
- * 
- *
- * User: dcheryasov - */ -public class PyCallByClassInspection extends PyInspection { - - @Override - public boolean isEnabledByDefault() { - return true; - } - - @NotNull - @Override - public HighlightDisplayLevel getDefaultLevel() { - return HighlightDisplayLevel.WEAK_WARNING; - } - - @NotNull - @Override - public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly, @NotNull LocalInspectionToolSession session) { - return new Visitor(holder, session); - } - - public static class Visitor extends PyInspectionVisitor { - - public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { - super(holder, session); - } - - - @Override - public void visitPyCallExpression(@NotNull PyCallExpression call) { - PyExpression callee = call.getCallee(); - if (callee instanceof PyQualifiedExpression) { - PyExpression qualifier = ((PyQualifiedExpression)callee).getQualifier(); - if (qualifier != null) { - PyType qual_type = myTypeEvalContext.getType(qualifier); - if (qual_type instanceof PyClassType) { - final PyClassType qual_class_type = (PyClassType)qual_type; - if (qual_class_type.isDefinition()) { - PyClass qual_class = qual_class_type.getPyClass(); - final PyCallExpression.PyArgumentsMapping mapping = ContainerUtil.getFirstItem(call.multiMapArguments(getResolveContext())); - final PyCallableType callableType = mapping == null ? null : mapping.getCallableType(); - if (callableType != null && callableType.getModifier() != STATICMETHOD) { - final PyCallableParameter firstParameter = - ContainerUtil.getFirstItem(callableType.getParameters(myTypeEvalContext)); - if (firstParameter != null) { - for (Map.Entry entry : mapping.getMappedParameters().entrySet()) { - // we ignore *arg and **arg which we cannot analyze - if (firstParameter.equals(entry.getValue())) { - PyExpression first_arg = entry.getKey(); - assert first_arg != null; - PyType first_arg_type = myTypeEvalContext.getType(first_arg); - if (first_arg_type instanceof PyClassType) { - final PyClassType first_arg_class_type = (PyClassType)first_arg_type; - if (first_arg_class_type.isDefinition() && callableType.getModifier() != CLASSMETHOD) { - registerProblem( - first_arg, - PyPsiBundle.message("INSP.different.class.call.instance.of.class.expected.not.class.itself", qual_class.getQualifiedName()) - ); - } - PyClass first_arg_class = first_arg_class_type.getPyClass(); - if (first_arg_class != qual_class) { - // delegating to a parent is fine - if (callableType.getCallable() instanceof PyFunction) { - PyCallable callable = PsiTreeUtil.getParentOfType(call, PyCallable.class); - if (callable != null) { - PyFunction method = callable.asMethod(); - if (method != null) { - PyClass calling_class = method.getContainingClass(); - assert calling_class != null; // it's a method - if (first_arg_class.isSubclass(qual_class, myTypeEvalContext) && - calling_class.isSubclass(qual_class, myTypeEvalContext)) { - break; - // TODO: might propose to switch to super() here - } - } - } - } - // otherwise, it's not - registerProblem( - first_arg, - PyPsiBundle.message( - "INSP.different.class.call.passing.class.instead.class", - first_arg_class.getQualifiedName(), qual_class.getQualifiedName() - ) - ); - } - } - break; // once we found the first parameter, we don't need the rest - } - } - } - } - } - } - } - } - } - } -} diff --git a/python/testData/inspections/PyCallByClassInspection/test.py b/python/testData/inspections/PyCallByClassInspection/test.py deleted file mode 100644 index 902e32dea3de..000000000000 --- a/python/testData/inspections/PyCallByClassInspection/test.py +++ /dev/null @@ -1,23 +0,0 @@ -class Z(object): - def method(self): - pass - -class A(object): - def method(self, *args, **kwargs): - Z.method(self) # passing wrong instance - Z.method(Z) # passing class instead of instance - Z.method(A) # passing class instead of instance AND wrong class - Z.method(Z()) #pass - - def __init__(self): - pass - -class B(A): - def __init__(self): - A.__init__(self) # pass - -A.method(B()) - - -# PY-21083 -float.fromhex('A') \ No newline at end of file diff --git a/python/testData/inspections/PyTypeCheckerInspection/CallByClass.py b/python/testData/inspections/PyTypeCheckerInspection/CallByClass.py new file mode 100644 index 000000000000..ac58d747bdef --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/CallByClass.py @@ -0,0 +1,19 @@ +class Z: + def method(self): + pass + +class A: + def method(self): + Z.method(self) # passing wrong instance + Z.method(Z) # passing class instead of instance + Z.method(A) # passing class instead of instance AND wrong class + Z.method(Z()) #pass + + def __init__(self): + pass + +class B(A): + def __init__(self): + A.__init__(self) # pass + +A.method(B()) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java index 5f24dc6e0be7..7854edfa0c46 100644 --- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java +++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java @@ -4,7 +4,6 @@ package com.jetbrains.python; import com.intellij.codeInspection.InspectionProfileEntry; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; -import com.jetbrains.python.inspections.PyNonAsciiCharInspection; import com.jetbrains.python.documentation.docstrings.DocStringFormat; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.inspections.*; @@ -159,10 +158,6 @@ public class PythonInspectionsTest extends PyTestCase { myFixture.checkHighlighting(true, false, true); } - public void testPyCallByClassInspection() { - doHighlightingTest(PyCallByClassInspection.class); // ok, we can handle insanely long lines :) - } - private void doHighlightingTest(final Class inspectionClass) { myFixture.configureByFile("inspections/" + getTestName(false) + "/test.py"); myFixture.enableInspections(inspectionClass); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java index efed8f262f98..24e1c2216152 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java @@ -1255,4 +1255,8 @@ public class PyTypeCheckerInspectionTest extends PyInspectionTestCase { "func(42, None' (matched generic type '(T) -> None'), got '(x: str) -> None' instead\">accepts_anything)") ); } + + public void testCallByClass() { + doTest(); + } }