253 lines · cpp
1// RUN: %clang_analyze_cc1 -verify -analyzer-output=text %s \2// RUN: -analyzer-checker=core \3// RUN: -analyzer-checker=cplusplus \4// RUN: -analyzer-checker=unix5 6#include "Inputs/system-header-simulator-for-malloc.h"7 8//===----------------------------------------------------------------------===//9// unique_ptr test cases 10//===----------------------------------------------------------------------===//11namespace unique_ptr_tests {12 13// Custom unique_ptr implementation for testing14template <typename T>15struct unique_ptr {16 T* ptr;17 unique_ptr(T* p) : ptr(p) {}18 ~unique_ptr() {19 // This destructor intentionally doesn't delete 'ptr' to validate that the20 // heuristic trusts that smart pointers (based on their class name) will21 // release the pointee even if it doesn't understand their destructor.22 }23 unique_ptr(unique_ptr&& other) : ptr(other.ptr) { other.ptr = nullptr; }24 T* get() const { return ptr; }25};26 27template <typename T, typename... Args>28unique_ptr<T> make_unique(Args&&... args) {29 return unique_ptr<T>(new T(args...));30}31 32// Test 1: Check that we report leaks for malloc when passing smart pointers33void add_unique_ptr(unique_ptr<int> ptr) {34 // The unique_ptr destructor will be called when ptr goes out of scope35}36 37void test_malloc_with_smart_ptr() {38 void *ptr = malloc(4); // expected-note {{Memory is allocated}}39 40 add_unique_ptr(make_unique<int>(1));41 (void)ptr;42 // expected-warning@+1 {{Potential leak of memory pointed to by 'ptr'}} expected-note@+1 {{Potential leak of memory pointed to by 'ptr'}}43}44 45// Test 2: Check that we don't report leaks for unique_ptr in temporary objects46struct Foo { 47 unique_ptr<int> i;48};49 50void add_foo(Foo foo) {51 // The unique_ptr destructor will be called when foo goes out of scope52}53 54void test_temporary_object() {55 // No warning should be emitted for this - the memory is managed by unique_ptr 56 // in the temporary Foo object, which will properly clean up the memory57 add_foo({make_unique<int>(1)});58}59 60// Test 3: Check that we don't report leaks for smart pointers in base class fields61struct Base {62 unique_ptr<int> base_ptr;63 Base() : base_ptr(nullptr) {}64 Base(unique_ptr<int>&& ptr) : base_ptr(static_cast<unique_ptr<int>&&>(ptr)) {}65};66 67struct Derived : public Base {68 int derived_field;69 Derived() : Base(), derived_field(0) {}70 Derived(unique_ptr<int>&& ptr, int field) : Base(static_cast<unique_ptr<int>&&>(ptr)), derived_field(field) {}71};72 73void add_derived(Derived derived) {74 // The unique_ptr destructor will be called when derived goes out of scope75 // This should include the base_ptr field from the base class76}77 78void test_base_class_smart_ptr() {79 // No warning should be emitted for this - the memory is managed by unique_ptr 80 // in the base class field of the temporary Derived object81 add_derived(Derived(make_unique<int>(1), 42));82}83 84// Test 4: Check that we don't report leaks for multiple owning arguments85struct SinglePtr {86 unique_ptr<int> ptr;87 SinglePtr(unique_ptr<int>&& p) : ptr(static_cast<unique_ptr<int>&&>(p)) {}88};89 90struct MultiPtr {91 unique_ptr<int> ptr1;92 unique_ptr<int> ptr2;93 unique_ptr<int> ptr3;94 95 MultiPtr(unique_ptr<int>&& p1, unique_ptr<int>&& p2, unique_ptr<int>&& p3)96 : ptr1(static_cast<unique_ptr<int>&&>(p1))97 , ptr2(static_cast<unique_ptr<int>&&>(p2))98 , ptr3(static_cast<unique_ptr<int>&&>(p3)) {}99};100 101void addMultiple(SinglePtr single, MultiPtr multi) {102 // All unique_ptr destructors will be called when the objects go out of scope103 // This tests handling of multiple by-value arguments with smart pointer fields104}105 106void test_multiple_owning_args() {107 // No warning should be emitted - all memory is properly managed by unique_ptr108 // in the temporary objects, which will properly clean up the memory109 addMultiple(110 SinglePtr(make_unique<int>(1)),111 MultiPtr(make_unique<int>(2), make_unique<int>(3), make_unique<int>(4))112 );113}114 115// Test 5: Check that we DO report leaks for raw pointers in mixed ownership scenarios116struct MixedOwnership {117 unique_ptr<int> smart_ptr; // Should NOT leak (smart pointer managed)118 int *raw_ptr; // Should leak (raw pointer)119 120 MixedOwnership() : smart_ptr(make_unique<int>(1)), raw_ptr(new int(42)) {} // expected-note {{Memory is allocated}}121};122 123void consume(MixedOwnership obj) {124 // The unique_ptr destructor will be called when obj goes out of scope125 // But raw_ptr will leak!126}127 128void test_mixed_ownership() {129 // This should report a leak for raw_ptr but not for smart_ptr130 consume(MixedOwnership()); // expected-note {{Calling default constructor for 'MixedOwnership'}} expected-note {{Returning from default constructor for 'MixedOwnership'}}131} // expected-warning {{Potential memory leak}} expected-note {{Potential memory leak}}132 133// Test 6: Check that we handle direct smart pointer constructor calls correctly134void test_direct_constructor() {135 // Direct constructor call - should not leak136 int* raw_ptr = new int(42);137 unique_ptr<int> smart(raw_ptr); // This should escape the raw_ptr symbol138 // No leak should be reported here since smart pointer takes ownership139}140 141void test_mixed_direct_constructor() {142 int* raw1 = new int(1); 143 int* raw2 = new int(2); // expected-note {{Memory is allocated}}144 145 unique_ptr<int> smart(raw1); // This should escape raw1146 // raw2 should leak since it's not managed by any smart pointer147 int x = *raw2; // expected-warning {{Potential leak of memory pointed to by 'raw2'}} expected-note {{Potential leak of memory pointed to by 'raw2'}}148}149 150// Test 7: Multiple memory owning arguments - demonstrates addTransition API usage151void addMultipleOwningArgs(152 unique_ptr<int> ptr1,153 unique_ptr<int> ptr2, 154 unique_ptr<int> ptr3155) {156 // All unique_ptr destructors will be called when arguments go out of scope157 // This tests handling of multiple smart pointer parameters in a single call158}159 160void test_multiple_memory_owning_arguments() {161 // No warning should be emitted - all memory is properly managed by unique_ptr162 // This test specifically exercises the addTransition API with multiple owning arguments163 addMultipleOwningArgs(164 make_unique<int>(1),165 make_unique<int>(2), 166 make_unique<int>(3)167 );168}169 170} // namespace unique_ptr_tests171 172//===----------------------------------------------------------------------===//173// Variadic constructor test cases174//===----------------------------------------------------------------------===//175namespace variadic_constructor_tests {176 177// Variadic constructor - test for potential out-of-bounds access178// This is the only test in this namespace and tests a scenario where Call.getNumArgs() > CD->getNumParams()179// We use a synthetic unique_ptr here to activate the specific logic in the MallocChecker that will test out of bounds180template <typename T>181struct unique_ptr {182 T* ptr;183 184 // Constructor with ellipsis - can receive more arguments than parameters 185 unique_ptr(T* p, ...) : ptr(p) {}186 187 ~unique_ptr() {188 // This destructor intentionally doesn't delete 'ptr' to validate that the189 // heuristic trusts that smart pointers (based on their class name) will190 // release the pointee even if it doesn't understand their destructor.191 }192};193 194void process_variadic_smart_ptr(unique_ptr<int> ptr) {195 // Function body doesn't matter for this test196}197 198void test_variadic_constructor_bounds() {199 void *malloc_ptr = malloc(4); // expected-note {{Memory is allocated}}200 201 // This call creates a smart pointer with more arguments than formal parameters202 // The constructor has 1 formal parameter (T* p) plus ellipsis, but we pass multiple args203 // This should trigger the bounds checking issue in handleSmartPointerConstructorArguments204 int* raw_ptr = new int(42);205 process_variadic_smart_ptr(unique_ptr<int>(raw_ptr, 1, 2, 3, 4, 5));206 207 (void)malloc_ptr;208} // expected-warning {{Potential leak of memory pointed to by 'malloc_ptr'}}209 // expected-note@-1 {{Potential leak of memory pointed to by 'malloc_ptr'}}210 211} // namespace variadic_constructor_tests212 213//===----------------------------------------------------------------------===//214// shared_ptr test cases215//===----------------------------------------------------------------------===//216namespace shared_ptr_tests {217 218// Custom shared_ptr implementation for testing219template <typename T>220struct shared_ptr {221 T* ptr;222 shared_ptr(T* p) : ptr(p) {}223 ~shared_ptr() {224 // This destructor intentionally doesn't delete 'ptr' to validate that the225 // heuristic trusts that smart pointers (based on their class name) will226 // release the pointee even if it doesn't understand their destructor.227 }228 shared_ptr(shared_ptr&& other) : ptr(other.ptr) { other.ptr = nullptr; }229 T* get() const { return ptr; }230};231 232template <typename T, typename... Args>233shared_ptr<T> make_shared(Args&&... args) {234 return shared_ptr<T>(new T(args...));235}236 237// Test 1: Check that we don't report leaks for shared_ptr in temporary objects238struct Foo { 239 shared_ptr<int> i;240};241 242void add_foo(Foo foo) {243 // The shared_ptr destructor will be called when foo goes out of scope244}245 246void test_temporary_object() {247 // No warning should be emitted for this - the memory is managed by shared_ptr 248 // in the temporary Foo object, which will properly clean up the memory249 add_foo({make_shared<int>(1)});250}251 252} // namespace shared_ptr_tests253