diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index b945bfbd82..64d59ed16d 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1137,9 +1137,24 @@ def visit_classdef(self, node: nodes.ClassDef) -> None: """Visit class: update consumption analysis variable.""" self._to_consume.append(NamesConsumer(node, "class")) - def leave_classdef(self, _: nodes.ClassDef) -> None: + def leave_classdef(self, node: nodes.ClassDef) -> None: """Leave class: update consumption analysis variable.""" - # do not check for not used locals here (no sense) + # Check for hidden ancestor names + # e.g. "six" in: Class X(six.with_metaclass(ABCMeta, object)): + for name_node in node.nodes_of_class(nodes.Name): + if ( + isinstance(name_node.parent, nodes.Call) + and isinstance(name_node.parent.func, nodes.Attribute) + and isinstance(name_node.parent.func.expr, nodes.Name) + ): + hidden_name_node = name_node.parent.func.expr + for consumer in self._to_consume: + if hidden_name_node.name in consumer.to_consume: + consumer.mark_as_consumed( + hidden_name_node.name, + consumer.to_consume[hidden_name_node.name], + ) + break self._to_consume.pop() def visit_lambda(self, node: nodes.Lambda) -> None: diff --git a/tests/functional/u/unused/unused_import.py b/tests/functional/u/unused/unused_import.py index 143ee7cf3a..46907b47c5 100644 --- a/tests/functional/u/unused/unused_import.py +++ b/tests/functional/u/unused/unused_import.py @@ -3,6 +3,7 @@ import xml.etree # [unused-import] import xml.sax # [unused-import] import os.path as test # [unused-import] +from abc import ABCMeta from sys import argv as test2 # [unused-import] from sys import flags # [unused-import] # +1:[unused-import,unused-import] @@ -10,6 +11,7 @@ import re, html.parser # [unused-import] DATA = Counter() # pylint: disable=self-assigning-variable +import six from fake import SomeName, SomeOtherName # [unused-import] class SomeClass(object): SomeName = SomeName # https://bitbucket.org/logilab/pylint/issue/475 @@ -87,3 +89,6 @@ def blop(self): TYPE_CHECKING = False if TYPE_CHECKING: import zoneinfo + +class WithMetaclass(six.with_metaclass(ABCMeta, object)): + pass diff --git a/tests/functional/u/unused/unused_import.txt b/tests/functional/u/unused/unused_import.txt index 059405388e..cad621a599 100644 --- a/tests/functional/u/unused/unused_import.txt +++ b/tests/functional/u/unused/unused_import.txt @@ -1,14 +1,14 @@ unused-import:3:0:3:16::Unused import xml.etree:UNDEFINED unused-import:4:0:4:14::Unused import xml.sax:UNDEFINED unused-import:5:0:5:22::Unused os.path imported as test:UNDEFINED -unused-import:6:0:6:29::Unused argv imported from sys as test2:UNDEFINED -unused-import:7:0:7:21::Unused flags imported from sys:UNDEFINED -unused-import:9:0:9:51::Unused OrderedDict imported from collections:UNDEFINED -unused-import:9:0:9:51::Unused deque imported from collections:UNDEFINED -unused-import:10:0:10:22::Unused import re:UNDEFINED -unused-import:13:0:13:40::Unused SomeOtherName imported from fake:UNDEFINED -unused-import:48:0:48:9::Unused import os:UNDEFINED -unused-import:79:4:79:19::Unused import unittest:UNDEFINED -unused-import:81:4:81:15::Unused import uuid:UNDEFINED -unused-import:83:4:83:19::Unused import warnings:UNDEFINED -unused-import:85:4:85:21::Unused import compileall:UNDEFINED +unused-import:7:0:7:29::Unused argv imported from sys as test2:UNDEFINED +unused-import:8:0:8:21::Unused flags imported from sys:UNDEFINED +unused-import:10:0:10:51::Unused OrderedDict imported from collections:UNDEFINED +unused-import:10:0:10:51::Unused deque imported from collections:UNDEFINED +unused-import:11:0:11:22::Unused import re:UNDEFINED +unused-import:15:0:15:40::Unused SomeOtherName imported from fake:UNDEFINED +unused-import:50:0:50:9::Unused import os:UNDEFINED +unused-import:81:4:81:19::Unused import unittest:UNDEFINED +unused-import:83:4:83:15::Unused import uuid:UNDEFINED +unused-import:85:4:85:19::Unused import warnings:UNDEFINED +unused-import:87:4:87:21::Unused import compileall:UNDEFINED