// RUN: %clang_analyze_cc1 -analyzer-checker=webkit.RefCntblBaseVirtualDtor -verify %s

struct RefCntblBase {
  void ref() {}
  void deref() {}
};

template<class T>
struct DerivedClassTmpl1 : T { };
// expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl1<RefCntblBase>' but doesn't have virtual destructor}}

DerivedClassTmpl1<RefCntblBase> a;
void foo(DerivedClassTmpl1<RefCntblBase>& obj) { obj.deref(); }

template<class T>
struct DerivedClassTmpl2 : T { };
// expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl2<RefCntblBase>' but doesn't have virtual destructor}}

template<class T> int foo(T) { DerivedClassTmpl2<T> f; return 42; }
int b = foo(RefCntblBase{});


template<class T>
struct DerivedClassTmpl3 : T { };
// expected-warning@-1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl3<RefCntblBase>' but doesn't have virtual destructor}}

typedef DerivedClassTmpl3<RefCntblBase> Foo;
Foo c;

namespace WTF {

class RefCountedBase {
public:
  void ref() const { ++count; }

protected:
  bool derefBase() const
  {
    return !--count;
  }

private:
  mutable unsigned count;
};

template <typename T>
class RefCounted : public RefCountedBase {
public:
  void deref() const {
    if (derefBase())
      delete const_cast<T*>(static_cast<const T*>(this));
  }

protected:
  RefCounted() { }
};

template <typename X, typename T>
class ExoticRefCounted : public RefCountedBase {
public:
  void deref() const {
    if (derefBase())
      delete (const_cast<T*>(static_cast<const T*>(this)));
  }
};

template <typename X, typename T>
class BadBase : RefCountedBase {
public:
  void deref() const {
    if (derefBase())
      delete (const_cast<X*>(static_cast<const X*>(this)));
  }
};

template <typename T>
class FancyDeref {
public:
  void ref() const
  {
    ++refCount;
  }

  void deref() const
  {
    --refCount;
    if (refCount)
      return;
    auto deleteThis = [this] {
      delete static_cast<const T*>(this);
    };
    deleteThis();
  }
private:
  mutable unsigned refCount { 0 };
};

namespace Detail {

  template<typename Out, typename... In>
  class CallableWrapperBase {
  public:
    virtual ~CallableWrapperBase() { }
    virtual Out call(In...) = 0;
  };

  template<typename, typename, typename...> class CallableWrapper;

  template<typename CallableType, typename Out, typename... In>
  class CallableWrapper : public CallableWrapperBase<Out, In...> {
  public:
    explicit CallableWrapper(CallableType&& callable)
        : m_callable(WTFMove(callable)) { }
    CallableWrapper(const CallableWrapper&) = delete;
    CallableWrapper& operator=(const CallableWrapper&) = delete;
    Out call(In... in) final { return m_callable(in...); }
  private:
    CallableType m_callable;
  };

} // namespace Detail

template<typename> class Function;

template <typename Out, typename... In>
class Function<Out(In...)> {
public:
  using Impl = Detail::CallableWrapperBase<Out, In...>;

  Function() = default;

  template<typename CallableType>
  Function(CallableType&& callable)
      : m_callableWrapper(new Detail::CallableWrapper<CallableType, Out, In...>>(callable)) { }

  template<typename FunctionType>
  Function(FunctionType f)
      : m_callableWrapper(new Detail::CallableWrapper<FunctionType, Out, In...>>(f)) { }

  ~Function() {
  }

  Out operator()(In... in) const {
      ASSERT(m_callableWrapper);
      return m_callableWrapper->call(in...);
  }

  explicit operator bool() const { return !!m_callableWrapper; }

private:
  Impl* m_callableWrapper;
};

void ensureOnMainThread(const Function<void()>&& function);

enum class DestructionThread { Any, MainThread };

template <typename T, DestructionThread destructionThread = DestructionThread::Any>
class FancyDeref2 {
public:
  void ref() const
  {
    ++refCount;
  }

  void deref() const
  {
    --refCount;
    if (refCount)
      return;
    const_cast<FancyDeref2<T, destructionThread>*>(this)->destroy();
  }

private:
  void destroy() {
    delete static_cast<T*>(this);
  }
  mutable unsigned refCount { 0 };
};

template <typename S>
class DerivedFancyDeref2 : public FancyDeref2<S> {
};

template <typename T>
class BadFancyDeref {
public:
  void ref() const
  {
    ++refCount;
  }

  void deref() const
  {
    --refCount;
    if (refCount)
      return;
    auto deleteThis = [this] {
      delete static_cast<const T*>(this);
    };
    delete this;
  }
private:
  mutable unsigned refCount { 0 };
};

template <typename T>
class ThreadSafeRefCounted {
public:
  void ref() const { ++refCount; }
  void deref() const {
    if (!--refCount)
      delete const_cast<T*>(static_cast<const T*>(this));
  }
private:
  mutable unsigned refCount { 0 };
};

template <typename T>
class ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr {
public:
  void ref() const { ++refCount; }
  void deref() const {
    if (!--refCount)
      delete const_cast<T*>(static_cast<const T*>(this));
  }
private:
  mutable unsigned refCount { 0 };
};

} // namespace WTF

class DerivedClass4 : public WTF::RefCounted<DerivedClass4> { };

class DerivedClass4b : public WTF::ExoticRefCounted<int, DerivedClass4b> { };

class DerivedClass4cSub;
class DerivedClass4c : public WTF::BadBase<DerivedClass4cSub, DerivedClass4c> { };
// expected-warning@-1{{Class 'WTF::BadBase<DerivedClass4cSub, DerivedClass4c>' is used as a base of class 'DerivedClass4c' but doesn't have virtual destructor}}
class DerivedClass4cSub : public DerivedClass4c { };
void UseDerivedClass4c(DerivedClass4c &obj) { obj.deref(); }

class DerivedClass4d : public WTF::RefCounted<DerivedClass4d> {
public:
  virtual ~DerivedClass4d() { }
};
class DerivedClass4dSub : public DerivedClass4d { };

class DerivedClass5 : public DerivedClass4 { };
// expected-warning@-1{{Class 'DerivedClass4' is used as a base of class 'DerivedClass5' but doesn't have virtual destructor}}
void UseDerivedClass5(DerivedClass5 &obj) { obj.deref(); }

class DerivedClass6 : public WTF::ThreadSafeRefCounted<DerivedClass6> { };
void UseDerivedClass6(DerivedClass6 &obj) { obj.deref(); }

class DerivedClass7 : public DerivedClass6 { };
// expected-warning@-1{{Class 'DerivedClass6' is used as a base of class 'DerivedClass7' but doesn't have virtual destructor}}
void UseDerivedClass7(DerivedClass7 &obj) { obj.deref(); }

class DerivedClass8 : public WTF::ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<DerivedClass8> { };
void UseDerivedClass8(DerivedClass8 &obj) { obj.deref(); }

class DerivedClass9 : public DerivedClass8 { };
// expected-warning@-1{{Class 'DerivedClass8' is used as a base of class 'DerivedClass9' but doesn't have virtual destructor}}
void UseDerivedClass9(DerivedClass9 &obj) { obj.deref(); }

class DerivedClass10 : public WTF::FancyDeref<DerivedClass10> { };
void UseDerivedClass10(DerivedClass10 &obj) { obj.deref(); }

class DerivedClass10b : public WTF::DerivedFancyDeref2<DerivedClass10b> { };
void UseDerivedClass10b(DerivedClass10b &obj) { obj.deref(); }

class DerivedClass10c : public WTF::BadFancyDeref<DerivedClass10c> { };
// expected-warning@-1{{Class 'WTF::BadFancyDeref<DerivedClass10c>' is used as a base of class 'DerivedClass10c' but doesn't have virtual destructor}}
void UseDerivedClass10c(DerivedClass10c &obj) { obj.deref(); }

class BaseClass1 {
public:
  void ref() const { ++refCount; }
  void deref() const;
private:
  enum class Type { Base, Derived } type { Type::Base };
  mutable unsigned refCount { 0 };
};

class DerivedClass11 : public BaseClass1 { };

void BaseClass1::deref() const
{
  --refCount;
  if (refCount)
    return;
  switch (type) {
  case Type::Base:
    delete const_cast<BaseClass1*>(this);
    break;
  case Type::Derived:
    delete const_cast<DerivedClass11*>(static_cast<const DerivedClass11*>(this));
    break;
  }
}

void UseDerivedClass11(DerivedClass11& obj) { obj.deref(); }

class BaseClass2;
static void deleteBase2(BaseClass2*);

class BaseClass2 {
public:
  void ref() const { ++refCount; }
  void deref() const
  {
    if (!--refCount)
      deleteBase2(const_cast<BaseClass2*>(this));
  }
  virtual bool isDerived() { return false; }
private:
  mutable unsigned refCount { 0 };
};

class DerivedClass12 : public BaseClass2 {
  bool isDerived() final { return true; }
};

void UseDerivedClass11(DerivedClass12& obj) { obj.deref(); }

void deleteBase2(BaseClass2* obj) {
  if (obj->isDerived())
    delete static_cast<DerivedClass12*>(obj);
  else
    delete obj;
}

class BaseClass3 {
public:
  void ref() const { ++refCount; }
  void deref() const
  {
    if (!--refCount)
      const_cast<BaseClass3*>(this)->destroy();
  }
  virtual bool isDerived() { return false; }

private:
  void destroy();

  mutable unsigned refCount { 0 };
};

class DerivedClass13 : public BaseClass3 {
  bool isDerived() final { return true; }
};

void UseDerivedClass11(DerivedClass13& obj) { obj.deref(); }

void BaseClass3::destroy() {
  if (isDerived())
    delete static_cast<DerivedClass13*>(this);
  else
    delete this;
}

class RecursiveBaseClass {
public:
  void ref() const {
    if (otherObject)
      otherObject->ref();
    else
      ++refCount;
  }
  void deref() const {
    if (otherObject)
      otherObject->deref();
    else {
      --refCount;
      if (refCount)
        return;
      delete this;
    }
  }
private:
  RecursiveBaseClass* otherObject { nullptr };
  mutable unsigned refCount { 0 };
};

class RecursiveDerivedClass : public RecursiveBaseClass { };
// expected-warning@-1{{Class 'RecursiveBaseClass' is used as a base of class 'RecursiveDerivedClass' but doesn't have virtual destructor}}

class DerivedClass14 : public WTF::RefCounted<DerivedClass14> {
public:
  virtual ~DerivedClass14() { }
};

void UseDerivedClass14(DerivedClass14& obj) { obj.deref(); }

class DerivedClass15 : public DerivedClass14 { };

void UseDerivedClass15(DerivedClass15& obj) { obj.deref(); }
