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

#include "mock-types.h"

void *memcpy(void *dst, const void *src, unsigned int size);
void *malloc(unsigned int size);
void free(void *);

namespace WTF {

  template <typename T>
  class Vector {
  public:
    ~Vector() { destory(); }

    void append(const T& v)
    {
      if (m_size >= m_capacity)
        grow(m_capacity * 2);
      new (m_buffer + m_size) T();
      m_buffer[m_size] = v;
      m_size++;
    }

    void shrink(unsigned newSize)
    {
      unsigned currentSize = m_size;
      while (currentSize > newSize) {
        --currentSize;
        m_buffer[currentSize].~T();
      }
      m_size = currentSize;
    }

  private:
    void grow(unsigned newCapacity) {
      T* newBuffer = static_cast<T*>(malloc(sizeof(T) * newCapacity));
      memcpy(newBuffer, m_buffer, sizeof(T) * m_size);
      destory();
      m_buffer = newBuffer;
      m_capacity = newCapacity;
    }

    void destory() {
      if (!m_buffer)
        return;
      for (unsigned i = 0; i < m_size; ++i)
        m_buffer[i].~T();
      free(m_buffer);
      m_buffer = nullptr;
    }

    T* m_buffer { nullptr };
    unsigned m_size { 0 };
    unsigned m_capacity { 0 };
  };

} // namespace WTF

using WTF::Vector;

void someFunction();
RefCountable* [[clang::annotate_type("webkit.nodelete")]] safeFunction();

void functionWithoutNoDeleteAnnotation() {
  someFunction();
}

void [[clang::annotate_type("webkit.nodelete")]] callsUnsafe() {
  someFunction(); // expected-warning{{A function 'callsUnsafe' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

int* [[clang::annotate_type("webkit.nodelete")]] createsInt() {
  return new int;
}

void [[clang::annotate_type("webkit.nodelete")]] destroysInt(int* number) {
  delete number; // expected-warning{{A function 'destroysInt' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

struct IntPoint {
  int x { 0 };
  int y { 0 };
};

IntPoint* [[clang::annotate_type("webkit.nodelete")]] createsIntPoint() {
  return new IntPoint[2];
}

void [[clang::annotate_type("webkit.nodelete")]] destroysIntPoint(IntPoint* point) {
  delete[] point; // expected-warning{{A function 'destroysIntPoint' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

void [[clang::annotate_type("webkit.nodelete")]] callOperatorDelete(int* number) {
  ::operator delete(number); // expected-warning{{A function 'callOperatorDelete' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

void [[clang::annotate_type("webkit.nodelete")]] callsUnsafeWithSuppress();

[[clang::suppress]] void callsUnsafeWithSuppress() {
  someFunction();
}

void [[clang::annotate_type("webkit.nodelete")]] callsNoDeleteFunction() {
  callsUnsafeWithSuppress();
}

#define EXPORT_IMPORT __attribute__((visibility("default")))
EXPORT_IMPORT unsigned [[clang::annotate_type("webkit.nodelete")]] safeFunctionWithAttr();

void [[clang::annotate_type("webkit.nodelete")]] callsSafeWithAttribute() {
  unsigned r = safeFunctionWithAttr();
}

void [[clang::annotate_type("webkit.nodelete")]] callsSafe() {
  safeFunction();
}

void [[clang::annotate_type("webkit.nodelete")]] declWithNoDelete();
void declWithNoDelete() {
  someFunction(); // expected-warning{{A function 'declWithNoDelete' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}
void defWithNoDelete();
void [[clang::annotate_type("webkit.nodelete")]] defWithNoDelete() {
  someFunction(); // expected-warning{{A function 'defWithNoDelete' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

void [[clang::annotate_type("webkit.nodelete")]] funncWithUnsafeParam(Ref<RefCountable> t) {
  // expected-warning@-1{{A function 'funncWithUnsafeParam' has [[clang::annotate_type("webkit.nodelete")]] but it contains a parameter 't' which could destruct an object}}
}

void [[clang::annotate_type("webkit.nodelete")]] funncWithUnsafeParam(unsigned safe, Ref<RefCountable> unsafe) {
  // expected-warning@-1{{A function 'funncWithUnsafeParam' has [[clang::annotate_type("webkit.nodelete")]] but it contains a parameter 'unsafe' which could destruct an object}}
}

void [[clang::annotate_type("webkit.nodelete")]] funncWithUnsafeParam(Ref<RefCountable> unsafe, unsigned safe) {
  // expected-warning@-1{{A function 'funncWithUnsafeParam' has [[clang::annotate_type("webkit.nodelete")]] but it contains a parameter 'unsafe' which could destruct an object}}
}

void [[clang::annotate_type("webkit.nodelete")]] funncWithSafeParam(Ref<RefCountable>& safe1, Ref<RefCountable>* safe2) {
}

void [[clang::annotate_type("webkit.nodelete")]] callsUnsafeInDoWhile() {
  do {
    someFunction(); // expected-warning{{A function 'callsUnsafeInDoWhile' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  } while(0);
}

void [[clang::annotate_type("webkit.nodelete")]] callsUnsafeInIf(bool safe) {
  if (safe)
    safeFunction();
  else
    someFunction(); // expected-warning{{A function 'callsUnsafeInIf' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

void [[clang::annotate_type("webkit.nodelete")]] declaresUnsafeVar(bool safe) {
  if (safe) {
    auto* t = safeFunction();
  } else {
    RefPtr<RefCountable> t = safeFunction();
    // expected-warning@-1{{A function 'declaresUnsafeVar' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }
}

void [[clang::annotate_type("webkit.nodelete")]] declaresVarInIf(bool safe) {
  if (RefPtr<RefCountable> t = safeFunction()) {
    // expected-warning@-1{{A function 'declaresVarInIf' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
    t->method();
  }
}

template <typename T>
struct TemplatedClass {
  void [[clang::annotate_type("webkit.nodelete")]] methodCallsUnsafe(T* t) {
    t->method(); // expected-warning{{A function 'methodCallsUnsafe' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }
  void [[clang::annotate_type("webkit.nodelete")]] methodCallsSafe(T* t) {
    t->trivial();
  }
};

using TemplatedToRefCountable = TemplatedClass<RefCountable>;
void useTemplatedToRefCountable() {
  TemplatedToRefCountable c;
  c.methodCallsUnsafe(nullptr);
  c.methodCallsSafe(nullptr);
}

class WeakRefCountable : public CanMakeWeakPtr<WeakRefCountable> {
public:
  static Ref<WeakRefCountable> create();

  ~WeakRefCountable();

  void ref() { m_refCount++; }
  void deref() {
    m_refCount--;
    if (!m_refCount)
      delete this;
  }

private:
  WeakRefCountable();

  unsigned m_refCount { 0 };
};

class SomeClass {
public:

  void [[clang::annotate_type("webkit.nodelete")]] someMethod();
  void [[clang::annotate_type("webkit.nodelete")]] unsafeMethod() {
    someFunction(); // expected-warning{{A function 'unsafeMethod' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }
  void [[clang::annotate_type("webkit.nodelete")]] safeMethod() {
    safeFunction();
  }

  virtual void [[clang::annotate_type("webkit.nodelete")]] someVirtualMethod();
  virtual void [[clang::annotate_type("webkit.nodelete")]] unsafeVirtualMethod() {
    someFunction(); // expected-warning{{A function 'unsafeVirtualMethod' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }
  virtual void [[clang::annotate_type("webkit.nodelete")]] safeVirtualMethod() {
    safeFunction();
  }

  static void [[clang::annotate_type("webkit.nodelete")]] someStaticMethod();
  static void [[clang::annotate_type("webkit.nodelete")]] unsafeStaticMethod() {
    someFunction(); // expected-warning{{A function 'unsafeStaticMethod' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }
  static void [[clang::annotate_type("webkit.nodelete")]] safeStaticMethod() {
    safeFunction();
  }

  virtual void [[clang::annotate_type("webkit.nodelete")]] anotherVirtualMethod();

  void [[clang::annotate_type("webkit.nodelete")]] setObj(RefCountable* obj) {
    m_obj = obj; // expected-warning{{A function 'setObj' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  void [[clang::annotate_type("webkit.nodelete")]] swapObj(RefPtr<RefCountable>&& obj) {
    m_obj.swap(obj);
  }

  void [[clang::annotate_type("webkit.nodelete")]] assignObj(Ref<RefCountable>&& obj);

  void [[clang::annotate_type("webkit.nodelete")]] clearObj(RefCountable* obj) {
    m_obj = nullptr; // expected-warning{{A function 'clearObj' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  void [[clang::annotate_type("webkit.nodelete")]] deposeArg(WeakRefCountable&& unused) {
  }

  void [[clang::annotate_type("webkit.nodelete")]] deposeArgPtr(RefPtr<RefCountable>&& unused) {
  }

  enum class E : unsigned char { V1, V2 };
  bool [[clang::annotate_type("webkit.nodelete")]] deposeArgEnum() {
    E&& e = E::V1;
    return e != E::V2;
  }

  void [[clang::annotate_type("webkit.nodelete")]] deposeLocal() {
    RefPtr<RefCountable> obj = std::move(m_obj); // expected-warning{{A function 'deposeLocal' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  RefPtr<RefCountable> [[clang::annotate_type("webkit.nodelete")]] copyRefPtr() {
    return m_obj;
  }

  Ref<WeakRefCountable> [[clang::annotate_type("webkit.nodelete")]] copyRef() {
    return *m_weakObj.get();
  }

  RefPtr<WeakRefCountable> [[clang::annotate_type("webkit.nodelete")]] getWeakPtr() {
    return m_weakObj.get();
  }

  WeakRefCountable* [[clang::annotate_type("webkit.nodelete")]] useWeakPtr() {
    auto* localWeak = m_weakObj.get();
    return localWeak;
  }

private:
  RefPtr<RefCountable> m_obj;
  Ref<RefCountable> m_ref;
  WeakPtr<WeakRefCountable> m_weakObj;
};


void SomeClass::assignObj(Ref<RefCountable>&& obj) {
  m_obj = std::move(obj);
   // expected-warning@-1{{A function 'assignObj' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

class IntermediateClass : public SomeClass {
  void anotherVirtualMethod() override;
};

class DerivedClass : public IntermediateClass {
  void anotherVirtualMethod() override {
    someFunction(); // expected-warning{{A function 'anotherVirtualMethod' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }
};

template <class Type>
class Base {
public:
  virtual unsigned foo() const = 0;
};

template <class Type>
class Derived : public Base<Type> {
public:
  virtual unsigned foo() const { return 0; }
};

struct Data {
  static Ref<Data> [[clang::annotate_type("webkit.nodelete")]] create() {
    return adoptRef(*new Data);
  }

  static Ref<Data> [[clang::annotate_type("webkit.nodelete")]] create(double) {
    return adoptRef(*new Data(RefCountable::create()->next()));
    // expected-warning@-1{{A function 'create' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  static Data* [[clang::annotate_type("webkit.nodelete")]] create(int) {
    return adoptRef(new Data); // expected-warning{{A function 'create' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  static Ref<Data> create(const char*) {
    return std::move(adoptRef(*new Data));
  }

  void ref() {
    ++refCount;
  }

  void deref() {
    --refCount;
    if (!refCount)
      delete this;
  }

  virtual void doSomething() { }

  virtual void [[clang::annotate_type("webkit.nodelete")]] virtualWork() { }

  int a[3] { 0 };
  
protected:
  Data() = default;
  Data(RefCountable*) { }

private:
  unsigned refCount { 0 };
};

struct SubData : Data {
  static Ref<SubData> create() {
    return adoptRef(*new SubData);
  }

  void doSomething() override { }

  void virtualWork() override {
    someFunction();
    // expected-warning@-1{{A function 'virtualWork' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

private:
  SubData() = default;
};

void [[clang::annotate_type("webkit.nodelete")]] makeData() {
  RefPtr<Data> constantData[2] = { Data::create() };
  // expected-warning@-1{{A function 'makeData' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  RefPtr<Data> data[] = { Data::create() };
}

void [[clang::annotate_type("webkit.nodelete")]] makeSubData() {
  SubData::create()->doSomething();
  // expected-warning@-1{{A function 'makeSubData' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

struct ObjectWithConstructor {
  ObjectWithConstructor(double x) { }
  ObjectWithConstructor(float x) { }
  ObjectWithConstructor(decltype(nullptr)) { }
  ObjectWithConstructor(void*) { }
  ObjectWithConstructor(int x[3]) { }
  ObjectWithConstructor(void* x[2]) { }
  enum class E { V1, V2 };
  ObjectWithConstructor(E) { }
};

void [[clang::annotate_type("webkit.nodelete")]] makeObjectWithConstructor() {
  ObjectWithConstructor obj1(nullptr);
  ObjectWithConstructor obj2(0.5);
  double x = 0.7;
  ObjectWithConstructor obj3(x);
  int ints[] = { 1, 2, 3 };
  ObjectWithConstructor obj4(ints);
  void* ptrs[] = { nullptr, nullptr };
  ObjectWithConstructor obj5(ptrs);
  ObjectWithConstructor obj6(ObjectWithConstructor::E::V1);
}

struct ObjectWithNonTrivialDestructor {
  ~ObjectWithNonTrivialDestructor();
};

struct Container {
  Ref<Container> create() { return adoptRef(*new Container); }
  void ref() const { refCount++; }
  void deref() const {
    refCount--;
    if (!refCount)
      delete this;
  }

  ObjectWithNonTrivialDestructor obj;

private:
  mutable unsigned refCount { 0 };

  Container() = default;
};

struct SubContainer : public Container {
};

struct OtherContainerBase {
  ObjectWithNonTrivialDestructor obj;
};

struct OtherContainer : public OtherContainerBase {
  Ref<OtherContainer> create() { return adoptRef(*new OtherContainer); }
  void ref() const { refCount++; }
  void deref() const {
    refCount--;
    if (!refCount)
      delete this;
  }

private:
  mutable unsigned refCount { 0 };

  OtherContainer() = default;
};

struct ObjectWithContainers {
  RefPtr<Container> container;
  RefPtr<SubContainer> subContainer;
  RefPtr<OtherContainer> otherContainer;

  void [[clang::annotate_type("webkit.nodelete")]] setContainer(Ref<Container>&& newContainer) {
    container = std::move(newContainer);
    // expected-warning@-1{{A function 'setContainer' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  void [[clang::annotate_type("webkit.nodelete")]] setSubContainer(Ref<SubContainer>&& newContainer) {
    subContainer = std::move(newContainer);
    // expected-warning@-1{{A function 'setSubContainer' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  void [[clang::annotate_type("webkit.nodelete")]] setOtherContainer(Ref<OtherContainer>&& newContainer) {
    otherContainer = std::move(newContainer);
    // expected-warning@-1{{A function 'setOtherContainer' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  Vector<Container> containerList;
  Vector<SubContainer> subContainerList;
  Vector<OtherContainer> otherContainerList;

  void [[clang::annotate_type("webkit.nodelete")]] shrinkVector1() {
    containerList.shrink(0);
    // expected-warning@-1{{A function 'shrinkVector1' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  void [[clang::annotate_type("webkit.nodelete")]] shrinkVector2() {
    subContainerList.shrink(0);
    // expected-warning@-1{{A function 'shrinkVector2' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  void [[clang::annotate_type("webkit.nodelete")]] shrinkVector3() {
    otherContainerList.shrink(0);
    // expected-warning@-1{{A function 'shrinkVector3' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }
};

struct SomeObject {
  void ref() const;
  void deref() const;
  
  void doTrivialWork() { }

  void [[clang::annotate_type("webkit.nodelete")]] deleteItems() {
    delete[] m_items;
    // expected-warning@-1{{A function 'deleteItems' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

private:
  SomeObject* m_items;
};

void [[clang::annotate_type("webkit.nodelete")]] deleteArray(SomeObject* obj) {
  delete[] obj;
  // expected-warning@-1{{A function 'deleteArray' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

template <typename Callback>
void callLambda(Callback callback) {
  callback();
}

template <typename Callback>
void noopWithLambda(Callback) {
}

void [[clang::annotate_type("webkit.nodelete")]] deleteInLambda(SomeObject* someObj) {
  callLambda([&]() {
    // expected-warning@-1{{A function 'deleteInLambda' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
    delete someObj;
  });
}

void [[clang::annotate_type("webkit.nodelete")]] deleteInLambda2(SomeObject* someObj) {
  noopWithLambda([&]() {
    // expected-warning@-1{{A function 'deleteInLambda2' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
    delete someObj;
  });
}

void [[clang::annotate_type("webkit.nodelete")]] deleteIfNeeded(SomeObject* someObj) {
  if (someObj)
    delete someObj;
    // expected-warning@-1{{A function 'deleteIfNeeded' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

struct MemberAssignment {
public:
  void [[clang::annotate_type("webkit.nodelete")]] clearMember() {
    m_someObject = nullptr;
    // expected-warning@-1{{A function 'clearMember' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  void [[clang::annotate_type("webkit.nodelete")]] assignMember(SomeObject* ptr) {
    m_someObject = ptr;
    // expected-warning@-1{{A function 'assignMember' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }
  
  RefPtr<SomeObject> [[clang::annotate_type("webkit.nodelete")]] takeMember() {
    return std::exchange(m_someObject, nullptr);
    // expected-warning@-1{{A function 'takeMember' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  void [[clang::annotate_type("webkit.nodelete")]] takeAsTemp() {
    takeMember();
    // expected-warning@-1{{A function 'takeAsTemp' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

  void [[clang::annotate_type("webkit.nodelete")]] takeToLocalVar() {
    if (RefPtr ptr = std::exchange(m_someObject, nullptr))
      // expected-warning@-1{{A function 'takeToLocalVar' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
      ptr->doTrivialWork();
  }

  void [[clang::annotate_type("webkit.nodelete")]] takeObjectsAsTemp() {
    std::exchange(m_objects, { });
    // expected-warning@-1{{A function 'takeObjectsAsTemp' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
  }

private:
  RefPtr<SomeObject> m_someObject;
  Vector<Ref<SomeObject>> m_objects;
};

namespace copy_elision_edge_cases {

// These cases all inhibit NRVO/copy elision (so a real move or copy constructor runs into the return slot),
// but none of them perform any local destruction:
//   - Moved-from operands are emptied; their dtors are no-ops at the caller.
//   - Globals/statics are not destructed here at all.
//   - The return-slot temporary is destructed by the caller, not by us.
// All the mock Ref<T> copy/move ctors only manipulate pointers and a
// refcount, so the trivial-ctor analysis correctly classifies these as
// safe. The tests below document that the checker accepts each shape.

Ref<RefCountable> [[clang::annotate_type("webkit.nodelete")]] returnStdMoved(Ref<RefCountable>&& obj) {
  return std::move(obj);
}

Ref<RefCountable> [[clang::annotate_type("webkit.nodelete")]] returnFromBranches(bool b, Ref<RefCountable>&& a, Ref<RefCountable>&& c) {
  if (b)
    return std::move(a);
  return std::move(c);
}

Ref<RefCountable> [[clang::annotate_type("webkit.nodelete")]] returnDerefedParam(Ref<RefCountable>* param) {
  return *param;
}

// Returning a by-value parameter also requires a real copy/move construction.
// The function is still flagged here because of unsafe-parameter diagnostic that fires for the parameter declaration.
Ref<RefCountable> [[clang::annotate_type("webkit.nodelete")]] returnByValueParam(Ref<RefCountable> param) {
  // expected-warning@-1{{A function 'returnByValueParam' has [[clang::annotate_type("webkit.nodelete")]] but it contains a parameter 'param' which could destruct an object}}
  return param;
}

extern Ref<RefCountable> g_ref;
Ref<RefCountable> [[clang::annotate_type("webkit.nodelete")]] returnGlobal() {
  return g_ref;
}

struct StaticHolder {
  static Ref<RefCountable> s_ref;
};
Ref<RefCountable> [[clang::annotate_type("webkit.nodelete")]] returnClassStatic() {
  return StaticHolder::s_ref;
}

} // namespace copy_elision_edge_cases

namespace temp_object_typecheck {

struct Tracked {
  Tracked();
  ~Tracked();
};

Tracked [[clang::annotate_type("webkit.nodelete")]] makeTracked();

struct Box {
  Box(const Tracked&) {}
};

Box [[clang::annotate_type("webkit.nodelete")]] makeBox() {
  return Box(makeTracked());
  // expected-warning@-1{{A function 'makeBox' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

} // namespace temp_object_typecheck

namespace argument_temporaries_are_not_elided {

// Only a *returned* prvalue is elided into the caller's return slot. A
// smart-pointer temporary passed as a call argument is destructed in this
// function at the end of the full-expression (the caller destroys arguments),
// so its destructor -- which may run delete -- is correctly flagged, no matter
// how the callee binds it (by value, by rvalue reference, or by const
// reference). The factory and sinks are annotated no-delete so the only
// possible offender is the argument temporary's destruction.

Ref<RefCountable> [[clang::annotate_type("webkit.nodelete")]] makeRef();
void [[clang::annotate_type("webkit.nodelete")]] sinkByValue(Ref<RefCountable>);
void [[clang::annotate_type("webkit.nodelete")]] sinkByRvalueRef(Ref<RefCountable>&&);
void [[clang::annotate_type("webkit.nodelete")]] observeByConstRef(const Ref<RefCountable>&);

// Returned prvalue: constructed into the caller's return slot -> no local
// destruction here.
Ref<RefCountable> [[clang::annotate_type("webkit.nodelete")]] returnedPrvalueIsElided() {
  return makeRef();
}

void [[clang::annotate_type("webkit.nodelete")]] passedByValueIsFlagged() {
  sinkByValue(makeRef());
  // expected-warning@-1{{A function 'passedByValueIsFlagged' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

void [[clang::annotate_type("webkit.nodelete")]] passedByRvalueRefIsFlagged() {
  sinkByRvalueRef(makeRef());
  // expected-warning@-1{{A function 'passedByRvalueRefIsFlagged' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

void [[clang::annotate_type("webkit.nodelete")]] passedByConstRefIsFlagged() {
  observeByConstRef(makeRef());
  // expected-warning@-1{{A function 'passedByConstRefIsFlagged' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

void [[clang::annotate_type("webkit.nodelete")]] discardedTemporaryIsFlagged() {
  makeRef();
  // expected-warning@-1{{A function 'discardedTemporaryIsFlagged' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
}

} // namespace argument_temporaries_are_not_elided

namespace returned_prvalue_typedef {

// A returned prvalue spelled through a typedef/alias is still the return-slot
// object and must be elided. The elision relies on a canonical, unqualified
// type comparison rather than exact QualType identity.

using RefRC = Ref<RefCountable>;
RefRC [[clang::annotate_type("webkit.nodelete")]] makeAlias();

Ref<RefCountable> [[clang::annotate_type("webkit.nodelete")]] returnTypedefPrvalue() {
  return makeAlias(); // no warning: the elided temporary is the return slot.
}

} // namespace returned_prvalue_typedef

namespace create_with_default_constructor {

  struct ObjectWithDefaultConstructorWithoutMemberVariables {
    void ref() const;
    void deref() const;

    static auto [[clang::annotate_type("webkit.nodelete")]] create() {
      return adoptRef(*new ObjectWithDefaultConstructorWithoutMemberVariables());
    }
  };

  struct ObjectWithDefaultConstructorWithPODMemberVariables {
    void ref() const;
    void deref() const;

    static auto [[clang::annotate_type("webkit.nodelete")]] create() {
      return adoptRef(*new ObjectWithDefaultConstructorWithPODMemberVariables());
    }

  private:
    int value { 0 };
    RefCountable* ptr { nullptr };
  };

  struct ObjectWithOpaqueCtor {
    ObjectWithOpaqueCtor();
  };

  struct ObjectWithDefaultConstructorWithOpaqueCtorMemberVariables {
    void ref() const;
    void deref() const;

    static auto [[clang::annotate_type("webkit.nodelete")]] create() {
      return adoptRef(*new ObjectWithDefaultConstructorWithOpaqueCtorMemberVariables());
      // expected-warning@-1{{A function 'create' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}}
    }

  private:
    ObjectWithOpaqueCtor obj;
  };

} // namespace create_with_default_constructor


namespace trivial_implicit_ctor_in_new_expr {

// 'new T()' with parens emits a CXXConstructExpr for T's implicit default
// ctor. That ctor has no body in the AST (the synthesized body is materialised
// only at codegen), but it is trivial by the C++ standard and runs no user
// code, so it cannot delete. Verify the fast-path treats it as trivial.
struct Plain { int x; };

void [[clang::annotate_type("webkit.nodelete")]] valueInitNew() {
  Plain* p = new Plain();
  (void)p;
}

} // namespace trivial_implicit_ctor_in_new_expr
