// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-output=text -verify -std=c++11 %s

// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:PureOnly=true -DPUREONLY=1 -analyzer-output=text -verify -std=c++11 %s

#include "virtualcall.h"

class A {
public:
  A();

  ~A(){};

  virtual int foo() = 0;
  virtual void bar() = 0;
  void f() {
    foo();
	// expected-warning-re@-1 {{{{^}}Call to pure virtual function during construction}}
	// expected-note-re@-2 {{{{^}}Call to pure virtual function during construction}}
  }
};

class B : public A {
public:
  B() { // expected-note {{Calling default constructor for 'A'}}
    foo(); 
#if !PUREONLY
  	// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
	// expected-note-re@-3 {{{{^}}This constructor of an object of type 'B' has not returned when the virtual method was called}}
  	// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
  }
  ~B();

  virtual int foo();
  virtual void bar() {
    foo(); 
#if !PUREONLY
  	// expected-warning-re@-2 {{{{^}}Call to virtual function during destruction}}
  	// expected-note-re@-3 {{{{^}}Call to virtual function during destruction}}
#endif
  } 
};

A::A() { 
  f(); 
// expected-note-re@-1 {{{{^}}This constructor of an object of type 'A' has not returned when the virtual method was called}}
// expected-note-re@-2 {{{{^}}Calling 'A::f'}}
}

B::~B() {
  this->B::foo(); // no-warning
  this->B::bar();
#if !PUREONLY
 	 // expected-note-re@-2 {{{{^}}This destructor of an object of type '~B' has not returned when the virtual method was called}}
 	 // expected-note-re@-3 {{{{^}}Calling 'B::bar'}}
#endif
  this->foo(); 
#if !PUREONLY
 	 // expected-warning-re@-2 {{{{^}}Call to virtual function during destruction}}
 	 // expected-note-re@-3 {{{{^}}This destructor of an object of type '~B' has not returned when the virtual method was called}}
 	 // expected-note-re@-4 {{{{^}}Call to virtual function during destruction}}
#endif
	
}

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

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

C::C() {
  f(foo()); 
#if !PUREONLY
  	// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
	// expected-note-re@-3 {{{{^}}This constructor of an object of type 'C' has not returned when the virtual method was called}}
  	// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
}

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(); }
#if !PUREONLY
 	 // expected-note-re@-2 2{{{{^}}Calling '~B'}}
#endif
  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();     
#if !PUREONLY
  	// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
	// expected-note-re@-3 {{{{^}}This constructor of an object of type 'H' has not returned when the virtual method was called}}
  	// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
    H &h = *this;
    h.f(); 
#if !PUREONLY
  	// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
  	// expected-note-re@-3 {{{{^}}This constructor of an object of type 'H' has not returned when the virtual method was called}}
  	// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
  }
};

class X {
public:
  X() {
    g(); 
#if !PUREONLY
  	// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
	// expected-note-re@-3 {{{{^}}This constructor of an object of type 'X' has not returned when the virtual method was called}}
  	// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
  }
  X(int i) {
    if (i > 0) {
#if !PUREONLY
	// expected-note-re@-2 {{{{^}}Taking true branch}}
	// expected-note-re@-3 {{{{^}}Taking false branch}}
#endif
      X x(i - 1);
#if !PUREONLY
	// expected-note-re@-2 {{{{^}}Calling constructor for 'X'}}
#endif
      x.g(); // no warning
    }
    g(); 
#if !PUREONLY
  	// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
	// expected-note-re@-3 {{{{^}}This constructor of an object of type 'X' has not returned when the virtual method was called}}
  	// expected-note-re@-4 {{{{^}}Call to virtual function during construction}}
#endif
  }
  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);
#if !PUREONLY
  	// expected-note-re@-2 {{{{^}}This constructor of an object of type 'M' has not returned when the virtual method was called}}
	// expected-note-re@-3 {{{{^}}Calling 'N::callFooOfM'}}
#endif
  }
  virtual void foo();
};
void N::callFooOfM(M *m) {
  m->foo(); 
#if !PUREONLY
  	// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
  	// expected-note-re@-3 {{{{^}}Call to virtual function during construction}}
#endif
}

class Y {
public:
  virtual void foobar();
  void fooY() {
    F f1;
    foobar(); 
#if !PUREONLY
  	// expected-warning-re@-2 {{{{^}}Call to virtual function during construction}}
  	// expected-note-re@-3 {{{{^}}Call to virtual function during construction}}
#endif
  }
  Y() { fooY(); }
#if !PUREONLY
  	// expected-note-re@-2 {{{{^}}This constructor of an object of type 'Y' has not returned when the virtual method was called}}
  	// expected-note-re@-3 {{{{^}}Calling 'Y::fooY'}}
#endif
};

int main() {
  B b;
#if PUREONLY
	//expected-note-re@-2 {{{{^}}Calling default constructor for 'B'}}
#else 
	//expected-note-re@-4 2{{{{^}}Calling default constructor for 'B'}}
#endif
  C c;
#if !PUREONLY
	//expected-note-re@-2 {{{{^}}Calling default constructor for 'C'}}
#endif
  D d;
  E e;
  F f;
  G g;
  H h;
  H h1(1);
#if !PUREONLY
	//expected-note-re@-2 {{{{^}}Calling constructor for 'H'}}
	//expected-note-re@-3 {{{{^}}Calling constructor for 'H'}}
#endif
  X x; 
#if !PUREONLY
	//expected-note-re@-2 {{{{^}}Calling default constructor for 'X'}}
#endif
  X x1(1);
#if !PUREONLY
	//expected-note-re@-2 {{{{^}}Calling constructor for 'X'}}
#endif
  M m;
#if !PUREONLY
	//expected-note-re@-2 {{{{^}}Calling default constructor for 'M'}}
#endif
  Y *y = new Y;
#if !PUREONLY
  //expected-note-re@-2 {{{{^}}Calling default constructor for 'Y'}}
#endif
  delete y;
  header::Z z;
#if !PUREONLY
	// expected-note-re@-2 {{{{^}}Calling default constructor for 'Z'}}
#endif
}
#if !PUREONLY
	//expected-note-re@-2 2{{{{^}}Calling '~E'}}
#endif

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(); }
}
