class A
{

  class C
  {
  public:
    virtual void insert(void *) {}
  };
  class C1 : public C
  {
  public:
    A *a;
  };
  class C2 : public C
  {
  };

  class B
  {
  public:
    C *c;
    B() {}
    B(C *c)
    {
      this->c = c;
    }
    void set(C *c) { this->c = c; }
    C *get() { return this->c; }
    static B *make(C *c)
    {
      return new B(c);
    }
  };

public:
  void f0()
  {
    C cc;
    C ct;
    cc.insert(nullptr);
    ct.insert(new C());
    sink(&cc); // no flow
    sink(&ct); // $ ast MISSING: ir
  }
  void f1()
  {
    C *c = new C();
    B *b = B::make(c);
    sink(b->c); // $ast,ir
  }

  void f2()
  {
    B *b = new B();
    b->set(new C1());
    sink(b->get());                // $ ast ir=55:12
    sink((new B(new C()))->get()); // $ ast,ir
  }

  void f3()
  {
    B *b1 = new B();
    B *b2;
    b2 = setOnB(b1, new C2());
    sink(b1->c); // no flow
    sink(b2->c); // $ ast ir=64:21
  }

  void f4()
  {
    B *b1 = new B();
    B *b2;
    b2 = setOnBWrap(b1, new C2());
    sink(b1->c); // no flow
    sink(b2->c); // $ ast ir=73:25
  }

  B *setOnBWrap(B *b1, C *c)
  {
    B *b2;
    b2 = setOnB(b1, c);
    return r() ? b1 : b2;
  }

  A::B *setOnB(B *b1, C *c)
  {
    if (r())
    {
      B *b2 = new B();
      b2->set(c);
      return b2;
    }
    return b1;
  }

  void f5()
  {
    A *a = new A();
    C1 *c1 = new C1();
    c1->a = a;
    f6(c1);
  }
  void f6(C *c)
  {
    if (C1 *c1 = dynamic_cast<C1 *>(c))
    {
      sink(c1->a); // $ ast,ir
    }
    C *cc;
    if (C2 *c2 = dynamic_cast<C2 *>(c))
    {
      cc = c2;
    }
    else
    {
      cc = new C1();
    }
    if (C1 *c1 = dynamic_cast<C1 *>(cc))
    {
      sink(c1->a); // $ SPURIOUS: ast,ir
    }
  }

  void f7(B *b)
  {
    b->set(new C());
  }
  void f8()
  {
    B *b = new B();
    f7(b);
    sink(b->c); // $ ast,ir
  }

  class D
  {
  public:
    A::B *b;

    D(A::B *b, bool x)
    {
      b->c = new C();
      this->b = x ? b : new B();
    }
  };

  void
  f9()
  {
    B *b = new B();
    D *d = new D(b, r());
    sink(d->b);    // $ ast,ir=143:25 ast,ir=150:12
    sink(d->b->c); // $ ast,ir
    sink(b->c);    // $ ast,ir
  }

  void f10()
  {
    B *b = new B();
    MyList *l1 = new MyList(b, new MyList(nullptr, nullptr));
    MyList *l2 = new MyList(nullptr, l1);
    MyList *l3 = new MyList(nullptr, l2);
    sink(l3->head);                   // no flow, b is nested beneath at least one ->next
    sink(l3->next->head);             // no flow
    sink(l3->next->next->head);       // $ ast,ir
    sink(l3->next->next->next->head); // no flow
    for (MyList *l = l3; l != nullptr; l = l->next)
    {
      sink(l->head); // $ ast,ir
    }
  }

  static void sink(void *o) {}
  bool r() { return reinterpret_cast<long long>(this) % 10 > 5; }

  class MyList
  {
  public:
    B *head;
    MyList *next;
    MyList(B *newHead, MyList *next)
    {
      head = newHead;
      this->next = next;
    }
  };
};
