Repository: pylint-dev/pylint

Issue:
`invalid-name` false positive on assignment to class attribute `tearDown`
### Bug description

```python
"""hi"""
import unittest

class BaseCase(unittest.TestCase):
    """hi"""
    def tearDown(self):
        pass

class SubCase(unittest.TestCase):
    """hi"""
    tearDown = BaseCase.tearDown  # [invalid-name] - false positive!
```


### Configuration

```ini
[BASIC]
# https://github.com/google/styleguide/blob/1ae7350b45445b3a3a289c5fe756ab7c80014fe5/pylintrc#L191
class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
```


### Command used

```shell
/tmp/pylint/venv/bin/pylint test.py
```


### Pylint output

```
************* Module test
test.py:11:4: C0103: Class attribute name "tearDown" doesn't conform to '^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$' pattern (invalid-name)

------------------------------------------------------------------
Your code has been rated at 8.33/10 (previous run: 8.33/10, +0.00)
```


### Expected behavior

```

--------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)
```

In the past, pylint had this behavior - it did not complain about `invalid-name` here.

a4aff971aeee98365be8b63e146f27e13e7d5873 is the first bad commit


### Pylint version

```shell
pylint 3.3.0-dev0
astroid 3.2.2
Python 3.11.8 (main, May 26 2024, 00:40:16) [GCC 13.2.0]
```


### OS / Environment

_No response_

### Additional dependencies

_No response_


Discussion:
Thanks for the bisect!



Patch:
diff --git a/doc/whatsnew/fragments/9765.false_positive b/doc/whatsnew/fragments/9765.false_positive
new file mode 100644
index 0000000000..c85df15eee
--- /dev/null
+++ b/doc/whatsnew/fragments/9765.false_positive
@@ -0,0 +1,4 @@
+Fix a regression that raised ``invalid-name`` on class attributes merely
+overriding invalid names from an ancestor.
+
+Closes #9765
diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py
index 68f5767206..3514829fb1 100644
--- a/pylint/checkers/base/name_checker/checker.py
+++ b/pylint/checkers/base/name_checker/checker.py
@@ -491,7 +491,9 @@ def visit_assignname(  # pylint: disable=too-many-branches
                         self._check_name("variable", node.name, node)
 
         # Check names defined in class scopes
-        elif isinstance(frame, nodes.ClassDef):
+        elif isinstance(frame, nodes.ClassDef) and not any(
+            frame.local_attr_ancestors(node.name)
+        ):
             if utils.is_enum_member(node) or utils.is_assign_name_annotated_with(
                 node, "Final"
             ):


Test patch:
diff --git a/tests/functional/i/invalid/invalid_name.py b/tests/functional/i/invalid/invalid_name.py
index 66ad1e77cd..28638bcb4c 100644
--- a/tests/functional/i/invalid/invalid_name.py
+++ b/tests/functional/i/invalid/invalid_name.py
@@ -102,3 +102,10 @@ def test_disable_mixed(
         """Invalid-name will still be raised for other arguments."""
         self.foo_bar = fooBar
         self.foo_bar2 = fooBar2
+
+    def tearDown(self): ...  # pylint: disable=invalid-name
+
+
+class FooBarSubclass(FooBar):
+    tearDown = FooBar.tearDown
+    tearDownNotInAncestor = None  # [invalid-name]
diff --git a/tests/functional/i/invalid/invalid_name.rc b/tests/functional/i/invalid/invalid_name.rc
new file mode 100644
index 0000000000..b2444c2d15
--- /dev/null
+++ b/tests/functional/i/invalid/invalid_name.rc
@@ -0,0 +1,2 @@
+[MAIN]
+class-attribute-naming-style=snake_case
diff --git a/tests/functional/i/invalid/invalid_name.txt b/tests/functional/i/invalid/invalid_name.txt
index 72f8fcd906..e8622f864a 100644
--- a/tests/functional/i/invalid/invalid_name.txt
+++ b/tests/functional/i/invalid/invalid_name.txt
@@ -6,3 +6,4 @@ invalid-name:66:0:66:68:a_very_very_very_long_function_name_WithCamelCase_to_mak
 invalid-name:74:23:74:29:FooBar.__init__:"Argument name ""fooBar"" doesn't conform to snake_case naming style":HIGH
 invalid-name:80:8:80:14:FooBar.func1:"Argument name ""fooBar"" doesn't conform to snake_case naming style":HIGH
 invalid-name:100:8:100:15:FooBar.test_disable_mixed:"Argument name ""fooBar2"" doesn't conform to snake_case naming style":HIGH
+invalid-name:111:4:111:25:FooBarSubclass:"Class attribute name ""tearDownNotInAncestor"" doesn't conform to snake_case naming style":HIGH