// 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 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(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 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 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 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& safe1, Ref* 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 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 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 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; void useTemplatedToRefCountable() { TemplatedToRefCountable c; c.methodCallsUnsafe(nullptr); c.methodCallsSafe(nullptr); } class WeakRefCountable : public CanMakeWeakPtr { public: static Ref 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&& obj) { m_obj.swap(obj); } void [[clang::annotate_type("webkit.nodelete")]] assignObj(Ref&& 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&& 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 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 [[clang::annotate_type("webkit.nodelete")]] copyRefPtr() { return m_obj; } Ref [[clang::annotate_type("webkit.nodelete")]] copyRef() { return *m_weakObj.get(); } RefPtr [[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 m_obj; Ref m_ref; WeakPtr m_weakObj; }; void SomeClass::assignObj(Ref&& 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 Base { public: virtual unsigned foo() const = 0; }; template class Derived : public Base { public: virtual unsigned foo() const { return 0; } }; struct Data { static Ref [[clang::annotate_type("webkit.nodelete")]] create() { return adoptRef(*new Data); } static Ref [[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 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 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 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::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 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 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; RefPtr subContainer; RefPtr otherContainer; void [[clang::annotate_type("webkit.nodelete")]] setContainer(Ref&& 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&& 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&& 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 containerList; Vector subContainerList; Vector 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 void callLambda(Callback callback) { callback(); } template 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 [[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 m_someObject; Vector> 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 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 [[clang::annotate_type("webkit.nodelete")]] returnStdMoved(Ref&& obj) { return std::move(obj); } Ref [[clang::annotate_type("webkit.nodelete")]] returnFromBranches(bool b, Ref&& a, Ref&& c) { if (b) return std::move(a); return std::move(c); } Ref [[clang::annotate_type("webkit.nodelete")]] returnDerefedParam(Ref* 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 [[clang::annotate_type("webkit.nodelete")]] returnByValueParam(Ref 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 g_ref; Ref [[clang::annotate_type("webkit.nodelete")]] returnGlobal() { return g_ref; } struct StaticHolder { static Ref s_ref; }; Ref [[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 [[clang::annotate_type("webkit.nodelete")]] makeRef(); void [[clang::annotate_type("webkit.nodelete")]] sinkByValue(Ref); void [[clang::annotate_type("webkit.nodelete")]] sinkByRvalueRef(Ref&&); void [[clang::annotate_type("webkit.nodelete")]] observeByConstRef(const Ref&); // Returned prvalue: constructed into the caller's return slot -> no local // destruction here. Ref [[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; RefRC [[clang::annotate_type("webkit.nodelete")]] makeAlias(); Ref [[clang::annotate_type("webkit.nodelete")]] returnTypedefPrvalue() { return makeAlias(); // no warning: the elided temporary is the return slot. } } // namespace returned_prvalue_typedef