Fixed function doesn't return anything inspection for decorated and overridden methods (PY-10883)

This commit is contained in:
Andrey Vlasovskikh
2013-09-25 17:29:14 +04:00
parent ac22df298a
commit 8fed6c91ee
4 changed files with 83 additions and 5 deletions
@@ -3,11 +3,10 @@ package com.jetbrains.python.inspections;
import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.util.containers.hash.HashMap;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.Callable;
import com.jetbrains.python.psi.PyAssignmentStatement;
import com.jetbrains.python.psi.PyCallExpression;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.search.PyOverridingMethodsSearch;
import com.jetbrains.python.psi.types.PyNoneType;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.PyTypeChecker;
@@ -16,6 +15,8 @@ import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* User: ktisha
*
@@ -41,6 +42,8 @@ public class PyNoneFunctionAssignmentInspection extends PyInspection {
private static class Visitor extends PyInspectionVisitor {
private final Map<PyFunction, Boolean> myHasInheritors = new HashMap<PyFunction, Boolean>();
public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) {
super(holder, session);
}
@@ -56,11 +59,30 @@ public class PyNoneFunctionAssignmentInspection extends PyInspection {
final PyTypeChecker.AnalyzeCallResults analyzeCallResults = PyTypeChecker.analyzeCall(((PyCallExpression)value), myTypeEvalContext);
if (analyzeCallResults != null) {
final Callable callable = analyzeCallResults.getCallable();
if (PySdkUtil.isElementInSkeletons(callable)) return;
if (PySdkUtil.isElementInSkeletons(callable)) {
return;
}
if (callable instanceof PyFunction) {
final PyFunction function = (PyFunction)callable;
// Currently we don't infer types returned by decorators
if (hasInheritors(function) || PyUtil.hasCustomDecorators(function)) {
return;
}
}
registerProblem(node, PyBundle.message("INSP.none.function.assignment", callee.getName()));
}
}
}
}
private boolean hasInheritors(@NotNull PyFunction function) {
final Boolean cached = myHasInheritors.get(function);
if (cached != null) {
return cached;
}
final boolean result = PyOverridingMethodsSearch.search(function, true).findFirst() != null;
myHasInheritors.put(function, result);
return result;
}
}
}
@@ -0,0 +1,31 @@
import abc
from abc import abstractmethod
def decorator(f):
return f
class C(object):
__metaclass__ = abc.ABCMeta
@abstractmethod
def foo(self):
pass
@abc.abstractmethod
def bar(self):
pass
@decorator
def baz(self):
pass
def quux(self):
pass
def test(self):
a = self.foo()
b = self.bar()
c = self.baz()
<weak_warning descr="Function 'quux' doesn't return anything">d = self.quux()</weak_warning>
@@ -0,0 +1,15 @@
class C(object):
def foo(self):
pass
def bar(self):
pass
def test(self):
x = self.foo()
<weak_warning descr="Function 'bar' doesn't return anything">y = self.bar()</weak_warning>
class D(C):
def foo(self):
return 2
@@ -27,6 +27,16 @@ public class PyNoneFunctionAssignmentInspectionTest extends PyTestCase {
doTest();
}
// PY-10883
public void testMethodWithInheritors() {
doTest();
}
// PY-10883
public void testDecoratedMethod() {
doTest();
}
private void doTest() {
myFixture.configureByFile("inspections/PyNoneFunctionAssignmentInspection/" + getTestName(true) + ".py");
myFixture.enableInspections(PyNoneFunctionAssignmentInspection.class);