From 29d6e847e99680fc5f3f4a76486c25c8ff0a9dd7 Mon Sep 17 00:00:00 2001
From: Artem Yurchenko <artemyurchenko@zoho.com>
Date: Tue, 10 Sep 2024 10:00:27 -0700
Subject: [PATCH] set proper parents for namedtuple's and enum's

- using "tuple" ClassDef for a base of 'namedtuple' instead of a
   Name. We're already doing it for "enum"s, and I don't know how to
   ensure that the Name actually refers to the actual tuple, and not
   something shadowing it in the scope. Removed the test that asserted
   that the base is a Name and not a ClassDef. If it is actually
   useful, it should be checked and reworked comprehensively across
   all nodes (cf. Enum).

it's a part of the campaign to get rid of non-module roots
---
 astroid/brain/brain_namedtuple_enum.py | 47 +++++++++++++-------------
 1 file changed, 23 insertions(+), 24 deletions(-)

diff --git a/astroid/brain/brain_namedtuple_enum.py b/astroid/brain/brain_namedtuple_enum.py
index 72a07c1187..91d64db87e 100644
--- a/astroid/brain/brain_namedtuple_enum.py
+++ b/astroid/brain/brain_namedtuple_enum.py
@@ -74,7 +74,9 @@ def _extract_namedtuple_arg_or_keyword(  # pylint: disable=inconsistent-return-s
 
 def infer_func_form(
     node: nodes.Call,
-    base_type: list[nodes.NodeNG],
+    base_type: nodes.NodeNG,
+    *,
+    parent: nodes.NodeNG,
     context: InferenceContext | None = None,
     enum: bool = False,
 ) -> tuple[nodes.ClassDef, str, list[str]]:
@@ -147,15 +149,11 @@ def infer_func_form(
         col_offset=node.col_offset,
         end_lineno=node.end_lineno,
         end_col_offset=node.end_col_offset,
-        parent=nodes.Unknown(),
+        parent=parent,
     )
-    # A typical ClassDef automatically adds its name to the parent scope,
-    # but doing so causes problems, so defer setting parent until after init
-    # see: https://github.com/pylint-dev/pylint/issues/5982
-    class_node.parent = node.parent
     class_node.postinit(
         # set base class=tuple
-        bases=base_type,
+        bases=[base_type],
         body=[],
         decorators=None,
     )
@@ -195,25 +193,16 @@ def infer_named_tuple(
     node: nodes.Call, context: InferenceContext | None = None
 ) -> Iterator[nodes.ClassDef]:
     """Specific inference function for namedtuple Call node."""
-    tuple_base_name: list[nodes.NodeNG] = [
-        nodes.Name(
-            name="tuple",
-            parent=node.root(),
-            lineno=0,
-            col_offset=0,
-            end_lineno=None,
-            end_col_offset=None,
-        )
-    ]
+    tuple_base: nodes.Name = _extract_single_node("tuple")
     class_node, name, attributes = infer_func_form(
-        node, tuple_base_name, context=context
+        node, tuple_base, parent=AstroidManager().adhoc_module, context=context
     )
+
     call_site = arguments.CallSite.from_call(node, context=context)
-    node = extract_node("import collections; collections.namedtuple")
-    try:
-        func = next(node.infer())
-    except StopIteration as e:
-        raise InferenceError(node=node) from e
+    func = util.safe_infer(
+        _extract_single_node("import collections; collections.namedtuple")
+    )
+    assert isinstance(func, nodes.NodeNG)
     try:
         rename = next(
             call_site.infer_argument(func, "rename", context or InferenceContext())
@@ -364,7 +353,17 @@ def value(self):
         __members__ = ['']
     """
     )
-    class_node = infer_func_form(node, [enum_meta], context=context, enum=True)[0]
+
+    # FIXME arguably, the base here shouldn't be the EnumMeta class definition
+    # itself, but a reference (Name) to it. Otherwise, the invariant that all
+    # children of a node have that node as their parent is broken.
+    class_node = infer_func_form(
+        node,
+        enum_meta,
+        parent=AstroidManager().adhoc_module,
+        context=context,
+        enum=True,
+    )[0]
     return iter([class_node.instantiate_class()])