diff --git a/ChangeLog b/ChangeLog index 64a300d002..921c86c615 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,6 +25,10 @@ Release Date: TBA Close PyCQA/pylint#2326 Close PyCQA/pylint#2021 +* ``assert`` only functions are properly inferred as returning ``None`` + + Close #668 + * Add support for Python 3.8's `NamedExpr` nodes, which is part of assignment expressions. Close #674 diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py index bd0b7bd419..ccea388565 100644 --- a/astroid/scoped_nodes.py +++ b/astroid/scoped_nodes.py @@ -1656,7 +1656,13 @@ def infer_call_result(self, caller=None, context=None): first_return = next(returns, None) if not first_return: - raise exceptions.InferenceError("Empty return iterator") + if self.body and isinstance(self.body[-1], node_classes.Assert): + yield node_classes.Const(None) + return + + raise exceptions.InferenceError( + "The function does not have any return statements" + ) for returnnode in itertools.chain((first_return,), returns): if returnnode.value is None: diff --git a/astroid/tests/unittest_inference.py b/astroid/tests/unittest_inference.py index 1f9c7e447f..59cc23a6be 100644 --- a/astroid/tests/unittest_inference.py +++ b/astroid/tests/unittest_inference.py @@ -5289,5 +5289,19 @@ def both_branches(): assert [third[0].value, third[1].value] == [1, 2] +def test_assert_last_function_returns_none_on_inference(): + code = """ + def check_equal(a, b): + res = do_something_with_these(a, b) + assert a == b == res + + check_equal(a, b) + """ + node = extract_node(code) + inferred = next(node.infer()) + assert isinstance(inferred, nodes.Const) + assert inferred.value is None + + if __name__ == "__main__": unittest.main()