PY-35698, PY-33565: Delete obsolete PyCallByClassInspection. Type checker inspection covers most of the cases since there is always some type inferred for cls/self.

IDEA-CR-68591

GitOrigin-RevId: fef66f2e1e4d17de50fcca248a3ddc957f3c316d
This commit is contained in:
Irina Fediaeva
2020-11-10 13:31:16 +00:00
committed by intellij-monorepo-bot
parent 207aaa0356
commit 1e8a086708
8 changed files with 23 additions and 178 deletions
@@ -125,7 +125,6 @@
<localInspection language="Python" shortName="PyAugmentAssignmentInspection" suppressId="PyAugmentAssignment" bundle="messages.PyPsiBundle" key="INSP.NAME.augment.assignment" groupKey="INSP.GROUP.python" enabledByDefault="false" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyAugmentAssignmentInspection"/>
<localInspection language="Python" shortName="PyBroadExceptionInspection" suppressId="PyBroadException" bundle="messages.PyPsiBundle" key="INSP.NAME.too.broad.exception.clauses" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyBroadExceptionInspection"/>
<localInspection language="Python" shortName="PyByteLiteralInspection" suppressId="PyByteLiteral" bundle="messages.PyPsiBundle" key="INSP.NAME.byte.literal" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyByteLiteralInspection"/>
<localInspection language="Python" shortName="PyCallByClassInspection" suppressId="PyCallByClass" bundle="messages.PyPsiBundle" key="INSP.NAME.different.class.call" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyCallByClassInspection"/>
<localInspection language="Python" shortName="PyCallingNonCallableInspection" suppressId="PyCallingNonCallable" bundle="messages.PyPsiBundle" key="INSP.NAME.calling.non.callable" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyCallingNonCallableInspection"/>
<localInspection language="Python" shortName="PyComparisonWithNoneInspection" suppressId="PyComparisonWithNone" bundle="messages.PyPsiBundle" key="INSP.NAME.comparison.with.none" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyComparisonWithNoneInspection"/>
<localInspection language="Python" shortName="PyDataclassInspection" suppressId="PyDataclass" bundle="messages.PyPsiBundle" key="INSP.NAME.dataclass.definition.and.usages" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyDataclassInspection"/>
@@ -1,12 +0,0 @@
<html>
<body>
This inspection checks for calls of a method by class while passing an instance of a different class
as <code>self</code> parameter:
<pre>
foo = Foo()
Bar.baz(foo, *more)
</pre>
Sometimes this may be intentional and correct.
But when unintentional, this leads to subtle bugs.
</body>
</html>
@@ -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
@@ -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.
* <br/>
* Not marked are cases of inheritance calls in old-style classes, like:<pre>
* class B(A):
* def foo(self):
* A.foo(self)
* </pre>
* <br/>
* 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<PyExpression, PyCallableParameter> 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
}
}
}
}
}
}
}
}
}
}
}
@@ -1,23 +0,0 @@
class Z(object):
def method(self):
pass
class A(object):
def method(self, *args, **kwargs):
Z.method(<weak_warning descr="Passing inspections.PyCallByClassInspection.test.A instead of inspections.PyCallByClassInspection.test.Z. Is this intentional?">self</weak_warning>) # passing wrong instance
Z.method(<weak_warning descr="An instance of inspections.PyCallByClassInspection.test.Z expected, not the class itself">Z</weak_warning>) # passing class instead of instance
Z.method(<weak_warning descr="Passing inspections.PyCallByClassInspection.test.A instead of inspections.PyCallByClassInspection.test.Z. Is this intentional?"><weak_warning descr="An instance of inspections.PyCallByClassInspection.test.Z expected, not the class itself">A</weak_warning></weak_warning>) # 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(<weak_warning descr="Passing inspections.PyCallByClassInspection.test.B instead of inspections.PyCallByClassInspection.test.A. Is this intentional?">B()</weak_warning>)
# PY-21083
float.fromhex('A')
@@ -0,0 +1,19 @@
class Z:
def method(self):
pass
class A:
def method(self):
Z.method(<warning descr="Expected type 'Z', got 'A' instead">self</warning>) # passing wrong instance
Z.method(<warning descr="Expected type 'Z', got 'Type[Z]' instead">Z</warning>) # passing class instead of instance
Z.method(<warning descr="Expected type 'Z', got 'Type[A]' instead">A</warning>) # 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())
@@ -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<? extends PyInspection> inspectionClass) {
myFixture.configureByFile("inspections/" + getTestName(false) + "/test.py");
myFixture.enableInspections(inspectionClass);
@@ -1255,4 +1255,8 @@ public class PyTypeCheckerInspectionTest extends PyInspectionTestCase {
"func(42, <warning descr=\"Expected type '(int) -> None' (matched generic type '(T) -> None'), got '(x: str) -> None' instead\">accepts_anything</warning>)")
);
}
public void testCallByClass() {
doTest();
}
}