diff options
author | Ashley Whetter <AWhetter@users.noreply.github.com> | 2019-05-26 00:29:11 -0700 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2019-05-26 09:29:11 +0200 |
commit | 1669bc3868b198a1e97fae4fa60e0d6cfaed53d9 (patch) | |
tree | 2e0b905f00ad0af81543b22329058afc31214039 /pylint/checkers | |
parent | a9cb1c740a9f3d570bc90b42d1d3015aee95712c (diff) | |
download | pylint-git-1669bc3868b198a1e97fae4fa60e0d6cfaed53d9.tar.gz |
Fixed false positives for function stubs (#2927)
Close #1581
Diffstat (limited to 'pylint/checkers')
-rw-r--r-- | pylint/checkers/base.py | 13 | ||||
-rw-r--r-- | pylint/checkers/utils.py | 12 | ||||
-rw-r--r-- | pylint/checkers/variables.py | 79 |
3 files changed, 69 insertions, 35 deletions
diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index e9aecb287..76ef09608 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -836,7 +836,15 @@ class BasicErrorChecker(_BasicChecker): def _check_redefinition(self, redeftype, node): """check for redefinition of a function / method / class name""" parent_frame = node.parent.frame() - defined_self = parent_frame[node.name] + # Ignore function stubs created for type information + defined_self = next( + ( + local + for local in parent_frame.locals[node.name] + if not utils.is_overload_stub(local) + ), + node, + ) if defined_self is not node and not astroid.are_exclusive(node, defined_self): # Additional checks for methods which are not considered @@ -847,6 +855,9 @@ class BasicErrorChecker(_BasicChecker): ): return + if utils.is_overload_stub(node): + return + dummy_variables_rgx = lint_utils.get_global_option( self, "dummy-variables-rgx", default=None ) diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 2f7402892..826181845 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -1225,3 +1225,15 @@ def is_subclass_of(child: astroid.ClassDef, parent: astroid.ClassDef) -> bool: except _NonDeducibleTypeHierarchy: continue return False + + +@lru_cache(maxsize=1024) +def is_overload_stub(node: astroid.node_classes.NodeNG) -> bool: + """Check if a node if is a function stub decorated with typing.overload. + + :param node: Node to check. + :returns: True if node is an overload function stub. False otherwise. + """ + return isinstance(node, astroid.FunctionDef) and decorated_with( + node, ["typing.overload"] + ) diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 4ae13cadf..26cb94555 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -968,6 +968,45 @@ class VariablesChecker(BaseChecker): regex = authorized_rgx return regex and regex.match(name) + def _check_unused_arguments(self, name, node, stmt, argnames): + is_method = node.is_method() + klass = node.parent.frame() + if is_method and isinstance(klass, astroid.ClassDef): + confidence = ( + INFERENCE if utils.has_known_bases(klass) else INFERENCE_FAILURE + ) + else: + confidence = HIGH + + if is_method: + # Don't warn for the first argument of a (non static) method + if node.type != "staticmethod" and name == argnames[0]: + return + # Don't warn for argument of an overridden method + overridden = overridden_method(klass, node.name) + if overridden is not None and name in overridden.argnames(): + return + if node.name in utils.PYMETHODS and node.name not in ( + "__init__", + "__new__", + ): + return + # Don't check callback arguments + if any( + node.name.startswith(cb) or node.name.endswith(cb) + for cb in self.config.callbacks + ): + return + # Don't check arguments of singledispatch.register function. + if utils.is_registered_in_singledispatch_function(node): + return + + # Don't check function stubs created only for type information + if utils.is_overload_stub(node): + return + + self.add_message("unused-argument", args=name, node=stmt, confidence=confidence) + def _check_is_unused(self, name, node, stmt, global_names, nonlocal_names): # pylint: disable=too-many-branches # Ignore some special names specified by user configuration. @@ -991,42 +1030,9 @@ class VariablesChecker(BaseChecker): argnames = list( itertools.chain(node.argnames(), [arg.name for arg in node.args.kwonlyargs]) ) - is_method = node.is_method() - klass = node.parent.frame() - if is_method and isinstance(klass, astroid.ClassDef): - confidence = ( - INFERENCE if utils.has_known_bases(klass) else INFERENCE_FAILURE - ) - else: - confidence = HIGH - # Care about functions with unknown argument (builtins) if name in argnames: - if is_method: - # Don't warn for the first argument of a (non static) method - if node.type != "staticmethod" and name == argnames[0]: - return - # Don't warn for argument of an overridden method - overridden = overridden_method(klass, node.name) - if overridden is not None and name in overridden.argnames(): - return - if node.name in utils.PYMETHODS and node.name not in ( - "__init__", - "__new__", - ): - return - # Don't check callback arguments - if any( - node.name.startswith(cb) or node.name.endswith(cb) - for cb in self.config.callbacks - ): - return - # Don't check arguments of singledispatch.register function. - if utils.is_registered_in_singledispatch_function(node): - return - self.add_message( - "unused-argument", args=name, node=stmt, confidence=confidence - ) + self._check_unused_arguments(name, node, stmt, argnames) else: if stmt.parent and isinstance( stmt.parent, (astroid.Assign, astroid.AnnAssign) @@ -1069,6 +1075,11 @@ class VariablesChecker(BaseChecker): self.add_message("unused-import", args=msg, node=stmt) return message_name = "unused-variable" + + # Don't check function stubs created only for type information + if utils.is_overload_stub(node): + return + self.add_message(message_name, args=name, node=stmt) def leave_functiondef(self, node): |