diff options
author | Claudiu Popa <pcmanticore@gmail.com> | 2019-07-18 10:34:28 +0200 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2019-07-18 10:34:28 +0200 |
commit | 558e5b10d14dd81522affc7695dabd36273c5b60 (patch) | |
tree | adcfbfc1364233b79a6550a1f0329ff853d73071 /pylint/checkers/classes.py | |
parent | f38bd0d1b5d2e601d2a6034b3f0d6ee11343e26e (diff) | |
download | pylint-git-558e5b10d14dd81522affc7695dabd36273c5b60.tar.gz |
Added a new check, ``invalid-overridden-method``
This check is emitted when we detect that a method is overridden
as a property or a property is overridden as a method. This can indicate
a bug in the application code that will trigger a runtime error.
Close #2670
Diffstat (limited to 'pylint/checkers/classes.py')
-rw-r--r-- | pylint/checkers/classes.py | 38 |
1 files changed, 34 insertions, 4 deletions
diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 31a3f574d..79724656a 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -56,6 +56,7 @@ from pylint.checkers.utils import ( is_comprehension, is_iterable, is_property_setter, + is_property_setter_or_deleter, is_protocol_class, node_frame_class, overrides_a_method, @@ -70,7 +71,7 @@ if sys.version_info >= (3, 0): else: NEXT_METHOD = "next" INVALID_BASE_CLASSES = {"bool", "range", "slice", "memoryview"} - +BUILTIN_DECORATORS = {"builtins.property", "builtins.classmethod"} # Dealing with useless override detection, with regard # to parameters vs arguments @@ -566,6 +567,13 @@ MSGS = { "relying on super() delegation to do the same thing as another method " "from the MRO.", ), + "W0236": ( + "Method %r was expected to be %r, found it instead as %r", + "invalid-overridden-method", + "Used when we detect that a method was overridden as a property " + "or the other way around, which could result in potential bugs at " + "runtime.", + ), "E0236": ( "Invalid object %r in __slots__, must contain only non empty strings", "invalid-slots-object", @@ -890,16 +898,18 @@ a metaclass class method.", for overridden in klass.local_attr_ancestors(node.name): # get astroid for the searched method try: - meth_node = overridden[node.name] + parent_function = overridden[node.name] except KeyError: # we have found the method but it's not in the local # dictionary. # This may happen with astroid build from living objects continue - if not isinstance(meth_node, astroid.FunctionDef): + if not isinstance(parent_function, astroid.FunctionDef): continue - self._check_signature(node, meth_node, "overridden", klass) + self._check_signature(node, parent_function, "overridden", klass) + self._check_invalid_overridden_method(node, parent_function) break + if node.decorators: for decorator in node.decorators.nodes: if isinstance(decorator, astroid.Attribute) and decorator.attrname in ( @@ -1064,6 +1074,26 @@ a metaclass class method.", if node.args.args and len(node.args.args) > 1 and decorated_with_property(node): self.add_message("property-with-parameters", node=node) + def _check_invalid_overridden_method(self, function_node, parent_function_node): + parent_is_property = decorated_with_property( + parent_function_node + ) or is_property_setter_or_deleter(parent_function_node) + current_is_property = decorated_with_property( + function_node + ) or is_property_setter_or_deleter(function_node) + if parent_is_property and not current_is_property: + self.add_message( + "invalid-overridden-method", + args=(function_node.name, "property", function_node.type), + node=function_node, + ) + elif not parent_is_property and current_is_property: + self.add_message( + "invalid-overridden-method", + args=(function_node.name, "method", "property"), + node=function_node, + ) + def _check_slots(self, node): if "__slots__" not in node.locals: return |