Repository: pylint-dev/pylint

Issue:
Weird __slots__ manip causes pylint to crash
### Bug description

When parsing the following ``a.py``:

<!--
 If sharing the code is not an option, please state so,
 but providing only the stacktrace would still be helpful.
 -->

```python
class Test:
    __slots__ = ["a", "b", "c"]

class TestChild(Test):
    __slots__ += ["d", "e", "f"]

t = TestChild()

print(t.__slots__)

```

### Command used

```shell
pylint a.py
```

### Pylint output

<details open>
    <summary>
        pylint crashed with a ``AstroidError`` and with the following stacktrace:
    </summary>

```python
Traceback (most recent call last):
  File "c:\Users\Matth\.vscode\extensions\ms-python.pylint-2023.10.1\bundled\libs\pylint\lint\pylinter.py", line 788, in _lint_file
    check_astroid_module(module)
  File "c:\Users\Matth\.vscode\extensions\ms-python.pylint-2023.10.1\bundled\libs\pylint\lint\pylinter.py", line 1017, in check_astroid_module
    retval = self._check_astroid_module(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\Matth\.vscode\extensions\ms-python.pylint-2023.10.1\bundled\libs\pylint\lint\pylinter.py", line 1069, in _check_astroid_module
    walker.walk(node)
  File "c:\Users\Matth\.vscode\extensions\ms-python.pylint-2023.10.1\bundled\libs\pylint\utils\ast_walker.py", line 94, in walk
    self.walk(child)
  File "c:\Users\Matth\.vscode\extensions\ms-python.pylint-2023.10.1\bundled\libs\pylint\utils\ast_walker.py", line 91, in walk
    callback(astroid)
  File "c:\Users\Matth\.vscode\extensions\ms-python.pylint-2023.10.1\bundled\libs\pylint\checkers\classes\class_checker.py", line 879, in visit_classdef
    self._check_slots(node)
  File "c:\Users\Matth\.vscode\extensions\ms-python.pylint-2023.10.1\bundled\libs\pylint\checkers\classes\class_checker.py", line 1492, in _check_slots
    for slots in node.ilookup("__slots__"):
  File "c:\Users\Matth\.vscode\extensions\ms-python.pylint-2023.10.1\bundled\libs\astroid\bases.py", line 194, in _infer_stmts
    raise InferenceError(
astroid.exceptions.InferenceError: Inference failed for all members of [<AssignName.__slots__ l.5 at 0x14ef5196a20>].

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "c:\Users\Matth\.vscode\extensions\ms-python.pylint-2023.10.1\bundled\libs\pylint\lint\pylinter.py", line 752, in _lint_files
    self._lint_file(fileitem, module, check_astroid_module)
  File "c:\Users\Matth\.vscode\extensions\ms-python.pylint-2023.10.1\bundled\libs\pylint\lint\pylinter.py", line 790, in _lint_file
    raise astroid.AstroidError from e
astroid.exceptions.AstroidError

```


</details>

### Expected behavior

No crash.

### Pylint version

```shell
pylint 3.0.2
astroid 3.0.1
Python 3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)]
```

### OS / Environment

win32 (Windows)


<!--
Please remove this part if you're not using any of
your dependencies in the example.
 -->



Discussion:

Patch:
diff --git a/doc/whatsnew/fragments/9814.bugfix b/doc/whatsnew/fragments/9814.bugfix
new file mode 100644
index 0000000000..0d635e6bfd
--- /dev/null
+++ b/doc/whatsnew/fragments/9814.bugfix
@@ -0,0 +1,3 @@
+Fix a crash when a subclass extends ``__slots__``.
+
+Closes #9814
diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py
index 9758d2450c..d7bd299105 100644
--- a/pylint/checkers/classes/class_checker.py
+++ b/pylint/checkers/classes/class_checker.py
@@ -1537,7 +1537,11 @@ def _has_valid_slots(self, node: nodes.ClassDef) -> bool:
         if "__slots__" not in node.locals:
             return False
 
-        for slots in node.ilookup("__slots__"):
+        try:
+            inferred_slots = tuple(node.ilookup("__slots__"))
+        except astroid.InferenceError:
+            return False
+        for slots in inferred_slots:
             # check if __slots__ is a valid type
             if isinstance(slots, util.UninferableBase):
                 return False
@@ -1555,7 +1559,11 @@ def _check_slots(self, node: nodes.ClassDef) -> None:
         if "__slots__" not in node.locals:
             return
 
-        for slots in node.ilookup("__slots__"):
+        try:
+            inferred_slots = tuple(node.ilookup("__slots__"))
+        except astroid.InferenceError:
+            return
+        for slots in inferred_slots:
             # check if __slots__ is a valid type
             if isinstance(slots, util.UninferableBase):
                 continue
@@ -1586,8 +1594,12 @@ def _check_slots(self, node: nodes.ClassDef) -> None:
 
     def _get_classdef_slots_names(self, node: nodes.ClassDef) -> list[str]:
 
-        slots_names = []
-        for slots in node.ilookup("__slots__"):
+        slots_names: list[str] = []
+        try:
+            inferred_slots = tuple(node.ilookup("__slots__"))
+        except astroid.InferenceError:  # pragma: no cover
+            return slots_names
+        for slots in inferred_slots:
             if isinstance(slots, nodes.Dict):
                 values = [item[0] for item in slots.items]
             else:


Test patch:
diff --git a/tests/functional/s/slots_checks.py b/tests/functional/s/slots_checks.py
index 5a0bc0630c..e0c76dbe45 100644
--- a/tests/functional/s/slots_checks.py
+++ b/tests/functional/s/slots_checks.py
@@ -187,3 +187,17 @@ class ClassWithEmptySlotsAndAnnotation:
     __slots__ = ()
 
     a: int
+
+
+# https://github.com/pylint-dev/pylint/issues/9814
+class SlotsManipulationTest:
+    __slots__ = ["a", "b", "c"]
+
+
+class TestChild(SlotsManipulationTest):
+    __slots__ += ["d", "e", "f"]  # pylint: disable=undefined-variable
+
+
+t = TestChild()
+
+print(t.__slots__)