summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <cpopa@cloudbasesolutions.com>2015-02-09 22:39:08 +0200
committerClaudiu Popa <cpopa@cloudbasesolutions.com>2015-02-09 22:39:08 +0200
commit3e8467396311c5668a192f7a94b08c02fe370b51 (patch)
tree87affa38a8ebef15f830935d1eed1df1b9b6d1d0
parentd2bdd8ab9b0ed7e2f2a568b04fc0f8d1a5238e8a (diff)
downloadpylint-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--ChangeLog3
-rw-r--r--checkers/classes.py41
-rw-r--r--test/unittest_checker_classes.py16
3 files changed, 40 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index fd8f174..d7e6a9f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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()