code: def _get_struct_union_enum_type(
      self, kind, type, name=None, nested=False
  ):
      # First, a level of caching on the exact 'type' node of the AST.
      # This is obscure, but needed because pycparser "unrolls" declarations
      # such as "typedef struct { } foo_t, *foo_p" and we end up with
      # an AST that is not a tree, but a DAG, with the "type" node of the
      # two branches foo_t and foo_p of the trees being the same node.
      # It's a bit silly but detecting "DAG-ness" in the AST tree seems
      # to be the only way to distinguish this case from two independent
      # structs.  See test_struct_with_two_usages.
      try:
          return self._structnode2type[type]
      except KeyError:
          pass
      #
      # Note that this must handle parsing "struct foo" any number of
      # times and always return the same StructType object.  Additionally,
      # one of these times (not necessarily the first), the fields of
      # the struct can be specified with "struct foo { ...fields... }".
      # If no name is given, then we have to create a new anonymous struct
      # with no caching; in this case, the fields are either specified
      # right now or never.
      #
      force_name = name
      name = type.name
      #
      # get the type or create it if needed
      if name is None:
          # 'force_name' is used to guess a more readable name for
          # anonymous structs, for the common case "typedef struct { } foo".
          if force_name is not None:
              explicit_name = "$%s" % force_name
          else:
              self._anonymous_counter += 1
              explicit_name = "$%d" % self._anonymous_counter
          tp = None
      else:
          explicit_name = name
          key = "%s %s" % (kind, name)
          tp, _ = self._declarations.get(key, (None, None))
      #
      if tp is None:
          if kind == "struct":
              tp = model.StructType(explicit_name, None, None, None)
          elif kind == "union":
              tp = model.UnionType(explicit_name, None, None, None)
          elif kind == "enum":
              if explicit_name == "__dotdotdot__":
                  raise CDefError("Enums cannot be declared with ...")
              tp = self._build_enum_type(explicit_name, type.values)
          else:
              raise AssertionError("kind = %r" % (kind,))
          if name is not None:
              self._declare(key, tp)
      else:
          if kind == "enum" and type.values is not None:
              raise NotImplementedError(
                  "enum %s: the '{}' declaration should appear on the first "
                  "time the enum is mentioned, not later"
                  % explicit_name
              )
      if not tp.forcename:
          tp.force_the_name(force_name)
      if tp.forcename and "$" in tp.name:
          self._declare("anonymous %s" % tp.forcename, tp)
      #
      self._structnode2type[type] = tp
      #
      # enums: done here
      if kind == "enum":
          return tp
      #
      # is there a 'type.decls'?  If yes, then this is the place in the
      # C sources that declare the fields.  If no, then just return the
      # existing type, possibly still incomplete.
      if type.decls is None:
          return tp
      #
      if tp.fldnames is not None:
          raise CDefError("duplicate declaration of struct %s" % name)
      fldnames = []
      fldtypes = []
      fldbitsize = []
      fldquals = []
      for decl in type.decls:
          if (
              isinstance(decl.type, pycparser.c_ast.IdentifierType)
              and "".join(decl.type.names) == "__dotdotdot__"
          ):
              # XXX pycparser is inconsistent: 'names' should be a list
              # of strings, but is sometimes just one string.  Use
              # str.join() as a way to cope with both.
              self._make_partial(tp, nested)
              continue
          if decl.bitsize is None:
              bitsize = -1
          else:
              bitsize = self._parse_constant(decl.bitsize)
          self._partial_length = False
          type, fqual = self._get_type_and_quals(
              decl.type, partial_length_ok=True
          )
          if self._partial_length:
              self._make_partial(tp, nested)
          if isinstance(type, model.StructType) and type.partial:
              self._make_partial(tp, nested)
          fldnames.append(decl.name or "")
          fldtypes.append(type)
          fldbitsize.append(bitsize)
          fldquals.append(fqual)
      tp.fldnames = tuple(fldnames)
      tp.fldtypes = tuple(fldtypes)
      tp.fldbitsize = tuple(fldbitsize)
      tp.fldquals = tuple(fldquals)
      if fldbitsize != [-1] * len(fldbitsize):
          if isinstance(tp, model.StructType) and tp.partial:
              raise NotImplementedError(
                  "%s: using both bitfields and '...;'" % (tp,)
              )
      tp.packed = self._options.get("packed")
      if (
          tp.completed
      ):  # must be re-completed: it is not opaque any more
          tp.completed = 0
          self._recomplete.append(tp)
      return tp
  
imports: from .None import model
  from .commontypes import COMMON_TYPES, resolve_common_type
  from .error import FFIError, CDefError
  import weakref, re, sys
label_desc: Use image node_id 20 for calling the Parser obj's underlying member method code with example usage: obj._get_struct_union_enum_type(kind, type, name, nested) and returns: tp, self, tp, tp