// RUN: %clang_analyze_cc1 -analyzer-checker=core,optin.cplusplus.VirtualCall \
// RUN:                    -analyzer-checker=debug.ExprInspection \
// RUN:                    -std=c++11 -verify=impure %s

// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.PureVirtualCall \
// RUN:                    -analyzer-checker=debug.ExprInspection \
// RUN:                    -std=c++11 -verify=pure -std=c++11 %s

// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.PureVirtualCall \
// RUN:                    -analyzer-checker=optin.cplusplus.VirtualCall \
// RUN:                    -analyzer-checker=debug.ExprInspection \
// RUN:                    -std=c++11 -verify=pure,impure -std=c++11 %s

#include "virtualcall.h"

void clang_analyzer_warnIfReached();

class A {
public:
  A();

  ~A(){};

  virtual int foo() = 0;
  virtual void bar() = 0;
  void f() {
    foo(); // pure-warning{{Call to pure virtual method 'A::foo' during construction has undefined behavior}}
    clang_analyzer_warnIfReached(); // no-warning
  }
};

A::A() {
  f();
}

class B {
public:
  B() {
    foo(); // impure-warning {{Call to virtual method 'B::foo' during construction bypasses virtual dispatch}}
  }
  ~B();

  virtual int foo();
  virtual void bar() {
    foo(); // impure-warning {{Call to virtual method 'B::foo' during destruction bypasses virtual dispatch}}
  }
};

B::~B() {
  this->B::foo(); // no-warning
  this->B::bar();
  this->foo(); // impure-warning {{Call to virtual method 'B::foo' during destruction bypasses virtual dispatch}}
}

class C : public B {
public:
  C();
  ~C();

  virtual int foo();
  void f(int i);
};

C::C() {
  f(foo()); // impure-warning {{Call to virtual method 'C::foo' during construction bypasses virtual dispatch}}
}

class D : public B {
public:
  D() {
    foo(); // no-warning
  }
  ~D() { bar(); }
  int foo() final;
  void bar() final { foo(); } // no-warning
};

class E final : public B {
public:
  E() {
    foo(); // no-warning
  }
  ~E() { bar(); }
  int foo() override;
};

class F {
public:
  F() {
    void (F::*ptr)() = &F::foo;
    (this->*ptr)();
  }
  void foo();
};

class G {
public:
  G() {}
  virtual void bar();
  void foo() {
    bar(); // no warning
  }
};

class H {
public:
  H() : initState(0) { init(); }
  int initState;
  virtual void f() const;
  void init() {
    if (initState)
      f(); // no warning
  }

  H(int i) {
    G g;
    g.foo();
    g.bar(); // no warning
    f(); // impure-warning {{Call to virtual method 'H::f' during construction bypasses virtual dispatch}}
    H &h = *this;
    h.f(); // impure-warning {{Call to virtual method 'H::f' during construction bypasses virtual dispatch}}
  }
};

class X {
public:
  X() {
    g(); // impure-warning {{Call to virtual method 'X::g' during construction bypasses virtual dispatch}}
  }
  X(int i) {
    if (i > 0) {
      X x(i - 1);
      x.g(); // no warning
    }
    g(); // impure-warning {{Call to virtual method 'X::g' during construction bypasses virtual dispatch}}
  }
  virtual void g();
};

class M;
class N {
public:
  virtual void virtualMethod();
  void callFooOfM(M *);
};
class M {
public:
  M() {
    N n;
    n.virtualMethod(); // no warning
    n.callFooOfM(this);
  }
  virtual void foo();
};
void N::callFooOfM(M *m) {
  m->foo(); // impure-warning {{Call to virtual method 'M::foo' during construction bypasses virtual dispatch}}
}

class Y {
public:
  virtual void foobar();
  void fooY() {
    F f1;
    foobar(); // impure-warning {{Call to virtual method 'Y::foobar' during construction bypasses virtual dispatch}}
  }
  Y() { fooY(); }
};

int main() {
  B b;
  C c;
  D d;
  E e;
  F f;
  G g;
  H h;
  H h1(1);
  X x; 
  X x1(1);
  M m;
  Y *y = new Y;
  delete y;
  header::Z z;
}

namespace PR34451 {
struct a {
  void b() {
    a c[1];
    c->b();
  }
};

class e {
 public:
  void b() const;
};

class c {
  void m_fn2() const;
  e d[];
};

void c::m_fn2() const { d->b(); }
}
