/* Reduced from linux-5.10.162's drivers-base-bus.c  */
/* { dg-additional-options "-fno-delete-null-pointer-checks -O2" } */

#include "../../gcc.dg/analyzer/analyzer-decls.h"


typedef unsigned int __kernel_size_t;
typedef int __kernel_ssize_t;
typedef __kernel_size_t size_t;
typedef __kernel_ssize_t ssize_t;

struct list_head
{
  struct list_head *next, *prev;
};

struct kobject
{
  /* [...snip...] */
};

struct attribute
{
  /* [...snip...] */
};

static inline
void
sysfs_remove_file_ns(struct kobject* kobj,
                     const struct attribute* attr,
                     const void* ns)
{
}

static inline
void
sysfs_remove_file(struct kobject* kobj, const struct attribute* attr)
{
  sysfs_remove_file_ns(kobj, attr, NULL);
}

extern struct kobject*
kobject_get(struct kobject* kobj);

extern void
kobject_put(struct kobject* kobj);

struct kset
{
  struct list_head list;
  /* [...snip...] */
  struct kobject kobj;
  /* [...snip...] */
} __attribute__((__designated_init__));

static inline
struct kset*
to_kset(struct kobject* kobj)
{
  return kobj ? ({
    char* __mptr = (char*)(kobj);
    ((struct kset*)(__mptr - __builtin_offsetof(struct kset, kobj)));
  }) : NULL;
}

static inline
struct kset*
kset_get(struct kset* k)
{
  return k ? to_kset(kobject_get(&k->kobj)) : NULL;
}

static inline
void
kset_put(struct kset* k)
{
  kobject_put(&k->kobj);
}

struct bus_type
{
  /* [...snip...] */
  struct device* dev_root;
  /* [...snip...] */
  struct subsys_private* p;
  /* [...snip...] */
};

struct bus_attribute
{
  struct attribute attr;
  /* [...snip...] */
};

extern void
device_unregister(struct device* dev);

struct subsys_private
{
  struct kset subsys;
  /* [...snip...] */
};

static struct bus_type*
bus_get(struct bus_type* bus)
{
  if (bus) { /* { dg-bogus "check of 'bus' for NULL after already dereferencing it" } */
    kset_get(&bus->p->subsys);
    return bus;
  }
  return NULL;
}

static void
bus_put(struct bus_type* bus)
{
  if (bus)
    kset_put(&bus->p->subsys);
}

void
bus_remove_file(struct bus_type* bus, struct bus_attribute* attr)
{
  if (bus_get(bus)) {
    sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);
    bus_put(bus);
  }
}

extern ssize_t
drivers_autoprobe_show(struct bus_type* bus, char* buf);

extern ssize_t
drivers_autoprobe_store(struct bus_type* bus, const char* buf, size_t count);

extern struct bus_attribute bus_attr_drivers_autoprobe;

static void
remove_probe_files(struct bus_type* bus)
{
  bus_remove_file(bus, &bus_attr_drivers_autoprobe);
  /* [...snip...] */
}

void
bus_unregister(struct bus_type* bus)
{
  /* [...snip...] */
  if (bus->dev_root) /* { dg-bogus "pointer 'bus' is dereferenced here" } */
    device_unregister(bus->dev_root);
  /* [...snip...] */
  remove_probe_files(bus);
  /* [...snip...] */
}
