diff options
author | Claudiu Popa <cpopa@cloudbasesolutions.com> | 2015-02-09 22:39:08 +0200 |
---|---|---|
committer | Claudiu Popa <cpopa@cloudbasesolutions.com> | 2015-02-09 22:39:08 +0200 |
commit | 3e8467396311c5668a192f7a94b08c02fe370b51 (patch) | |
tree | 87affa38a8ebef15f830935d1eed1df1b9b6d1d0 | |
parent | d2bdd8ab9b0ed7e2f2a568b04fc0f8d1a5238e8a (diff) | |
download | pylint-3e8467396311c5668a192f7a94b08c02fe370b51.tar.gz |
Use all the inferred statements for the super-init-not-called check.
For the super-init-not-called check, ``next(expr.infer())`` was used, which
returned as the first statement an YES node, which resulted in the node
being skipped for processing. Inferring all the objects would have returned
the actual classes for which the parent was indeed called. This patch
changes that to a infer-all strategy, which should yield better results.
Closes issue #389.
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | checkers/classes.py | 41 | ||||
-rw-r--r-- | test/unittest_checker_classes.py | 16 |
3 files changed, 40 insertions, 20 deletions
@@ -16,6 +16,9 @@ ChangeLog for Pylint * Catch enchant errors and emit 'invalid-characters-in-docstring' when checking for spelling errors. Closes issue #469. + * Use all the inferred statements for the super-init-not-called + check. Closes issue #389. + 2015-01-16 -- 1.4.1 diff --git a/checkers/classes.py b/checkers/classes.py index 1a10c35..8e51a47 100644 --- a/checkers/classes.py +++ b/checkers/classes.py @@ -893,26 +893,27 @@ a metaclass class method.'} expr.expr.func.name == 'super': return try: - klass = next(expr.expr.infer()) - if klass is YES: - continue - # The infered klass can be super(), which was - # assigned to a variable and the `__init__` was called later. - # - # base = super() - # base.__init__(...) - - if (isinstance(klass, astroid.Instance) and - isinstance(klass._proxied, astroid.Class) and - is_builtin_object(klass._proxied) and - klass._proxied.name == 'super'): - return - try: - del not_called_yet[klass] - except KeyError: - if klass not in to_call: - self.add_message('non-parent-init-called', - node=expr, args=klass.name) + for klass in expr.expr.infer(): + if klass is YES: + continue + # The infered klass can be super(), which was + # assigned to a variable and the `__init__` + # was called later. + # + # base = super() + # base.__init__(...) + + if (isinstance(klass, astroid.Instance) and + isinstance(klass._proxied, astroid.Class) and + is_builtin_object(klass._proxied) and + klass._proxied.name == 'super'): + return + try: + del not_called_yet[klass] + except KeyError: + if klass not in to_call: + self.add_message('non-parent-init-called', + node=expr, args=klass.name) except astroid.InferenceError: continue for klass, method in six.iteritems(not_called_yet): diff --git a/test/unittest_checker_classes.py b/test/unittest_checker_classes.py index f35e5e3..f21cfc1 100644 --- a/test/unittest_checker_classes.py +++ b/test/unittest_checker_classes.py @@ -61,6 +61,22 @@ class VariablesCheckerTC(CheckerTestCase): with self.assertNoMessages(): self.checker.visit_function(node) + def test_super_init_not_called_regression(self): + # This should not emit a super-init-not-called + # warning. It previously did this, because + # ``next(node.infer())`` was used in that checker's + # logic and the first inferred node was an YES object, + # leading to this false positive. + node = test_utils.extract_node(""" + import ctypes + + class Foo(ctypes.BigEndianStructure): + def __init__(self): #@ + ctypes.BigEndianStructure.__init__(self) + """) + with self.assertNoMessages(): + self.checker.visit_function(node) + if __name__ == '__main__': unittest.main() |