#include <utility>

#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
#define MOZ_RUNINIT  __attribute__((annotate("moz_global_var")))

template <typename T>
class already_AddRefed {
public:
  already_AddRefed();
  T* mPtr;
};

template <typename T>
class RefPtr {
public:
  RefPtr();
  MOZ_IMPLICIT RefPtr(T* aIn);
  MOZ_IMPLICIT RefPtr(already_AddRefed<T> aIn);

  RefPtr(const RefPtr<T>& aOther) = default;
  RefPtr& operator=(const RefPtr<T>&)  = default;

  // We must define non-defaulted move operations as in the real RefPtr to make
  // the type non-trivially-copyable.
  RefPtr(RefPtr<T>&&);
  RefPtr& operator=(RefPtr<T>&&);

  void swap(RefPtr<T>& aOther);

  ~RefPtr();
  T* mPtr;
};

template <typename T>
class nsCOMPtr {
public:
  nsCOMPtr();
  MOZ_IMPLICIT nsCOMPtr(T* aIn);
  MOZ_IMPLICIT nsCOMPtr(already_AddRefed<T> aIn);
  ~nsCOMPtr();
  T* mPtr;
};

class Type {
public:
  static nsCOMPtr<Type> someStaticCOMPtr;

  void f(nsCOMPtr<Type> ignoredArgument, Type *param) {
    nsCOMPtr<Type> never_referenced;
    nsCOMPtr<Type> kfdg_t1(this);
    nsCOMPtr<Type> kfdg_t2 = this;
    nsCOMPtr<Type> kfdg_t3 = (this);

    nsCOMPtr<Type> kfdg_m1(p); // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m1', or explicitly pass 'kfdg_m1' to `mozilla::Unused`}}
    nsCOMPtr<Type> kfdg_m2 = p; // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m2', or explicitly pass 'kfdg_m2' to `mozilla::Unused`}}
    nsCOMPtr<Type> kfdg_m3(p);
    kfdg_m3.mPtr->f(nullptr, nullptr);
    nsCOMPtr<Type> kfdg_m4 = p;
    kfdg_m4.mPtr->f(nullptr, nullptr);

    nsCOMPtr<Type> kfdg_a1((already_AddRefed<Type>()));
    nsCOMPtr<Type> kfdg_a2 = already_AddRefed<Type>();

    nsCOMPtr<Type> kfdg_p1(param);
    nsCOMPtr<Type> kfdg_p2 = param;


    RefPtr<Type> never_referenced2;
    RefPtr<Type> kfdg_t4(this);
    RefPtr<Type> kfdg_t5 = this;
    RefPtr<Type> kfdg_t6 = (this);

    RefPtr<Type> kfdg_m5(p); // expected-error {{Unused "kungFuDeathGrip" 'RefPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m5', or explicitly pass 'kfdg_m5' to `mozilla::Unused`}}
    RefPtr<Type> kfdg_m6 = p; // expected-error {{Unused "kungFuDeathGrip" 'RefPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m6', or explicitly pass 'kfdg_m6' to `mozilla::Unused`}}
    RefPtr<Type> kfdg_m7(p);
    kfdg_m7.mPtr->f(nullptr, nullptr);
    RefPtr<Type> kfdg_m8 = p;
    kfdg_m8.mPtr->f(nullptr, nullptr);

    RefPtr<Type> kfdg_a3((already_AddRefed<Type>()));
    RefPtr<Type> kfdg_a4 = already_AddRefed<Type>();

    RefPtr<Type> kfdg_p3(param);
    RefPtr<Type> kfdg_p4 = param;
  }

  Type *p;
};

struct Type2 {
  void f() {
    mWeakRef->f(nullptr, nullptr);
  }

  void g() {
    RefPtr<Type> kfdg;
    kfdg.swap(mStrongRef);
    f();
  }

  void h() {
    RefPtr<Type> kfdg = std::move(mStrongRef);
    f();
  }

  RefPtr<Type> mStrongRef;
  Type* mWeakRef;
};

void f(nsCOMPtr<Type> ignoredArgument, Type *param) {
  nsCOMPtr<Type> never_referenced;
  Type t;
  // Type *p = nullptr;
  nsCOMPtr<Type> kfdg_m1(t.p); // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m1', or explicitly pass 'kfdg_m1' to `mozilla::Unused`}}
  nsCOMPtr<Type> kfdg_m2 = t.p; // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m2', or explicitly pass 'kfdg_m2' to `mozilla::Unused`}}
  nsCOMPtr<Type> kfdg_m3(t.p);
  kfdg_m3.mPtr->f(nullptr, nullptr);
  nsCOMPtr<Type> kfdg_m4 = t.p;
  kfdg_m4.mPtr->f(nullptr, nullptr);

  nsCOMPtr<Type> kfdg_a1((already_AddRefed<Type>()));
  nsCOMPtr<Type> kfdg_a2 = already_AddRefed<Type>();

  nsCOMPtr<Type> kfdg_p1(param);
  nsCOMPtr<Type> kfdg_p2 = param;


  RefPtr<Type> never_referenced2;
  RefPtr<Type> kfdg_m5(t.p); // expected-error {{Unused "kungFuDeathGrip" 'RefPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m5', or explicitly pass 'kfdg_m5' to `mozilla::Unused`}}
  RefPtr<Type> kfdg_m6 = t.p; // expected-error {{Unused "kungFuDeathGrip" 'RefPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m6', or explicitly pass 'kfdg_m6' to `mozilla::Unused`}}
  RefPtr<Type> kfdg_m7(t.p);
  kfdg_m7.mPtr->f(nullptr, nullptr);
  RefPtr<Type> kfdg_m8 = t.p;
  kfdg_m8.mPtr->f(nullptr, nullptr);

  RefPtr<Type> kfdg_a3((already_AddRefed<Type>()));
  RefPtr<Type> kfdg_a4 = already_AddRefed<Type>();

  RefPtr<Type> kfdg_p3(param);
  RefPtr<Type> kfdg_p4 = param;
}

MOZ_RUNINIT nsCOMPtr<Type> Type::someStaticCOMPtr(nullptr);
