// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLocalVarsChecker -verify %s #import "objc-mock-types.h" #import "mock-system-header.h" void someFunction(); extern "C" CFStringRef LocalGlobalCFString; extern "C" NSString *LocalGlobalNSString; namespace raw_ptr { void foo() { SomeObj *bar; // FIXME: later on we might warn on uninitialized vars too } void bar(SomeObj *) {} } // namespace raw_ptr namespace pointer { SomeObj *provide(); void foo_ref() { SomeObj *bar = provide(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} [bar doWork]; } bool bar_ref(SomeObj *obj) { return !!obj; } void cf_ptr() { CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 10); // expected-warning@-1{{Local variable 'array' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} CFArrayAppendValue(array, nullptr); } dispatch_queue_t provide_os(); void os_ptr() { auto queue = provide_os(); // expected-warning@-1{{Local variable 'queue' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} dispatch_queue_get_label(queue); } } // namespace pointer namespace guardian_scopes { SomeObj *provide(); void foo1() { RetainPtr foo = provide(); { SomeObj *bar = foo.get(); } } void foo2() { RetainPtr foo = provide(); // missing embedded scope here SomeObj *bar = foo.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} [bar doWork]; } void foo3() { RetainPtr foo = provide(); { { SomeObj *bar = foo.get(); } } } void foo4() { { RetainPtr foo = provide(); { SomeObj *bar = foo.get(); } } } CFMutableArrayRef provide_cf(); void foo5() { RetainPtr foo = provide_cf(); { CFMutableArrayRef bar = foo.get(); CFArrayAppendValue(bar, nullptr); } CFMutableArrayRef baz = foo.get(); // expected-warning@-1{{Local variable 'baz' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} CFArrayAppendValue(baz, nullptr); } dispatch_queue_t provide_os(); void foo6() { OSObjectPtr queue = provide_os(); { dispatch_queue_t bar = queue.get(); dispatch_queue_get_label(bar); } dispatch_queue_t baz = queue.get(); // expected-warning@-1{{Local variable 'baz' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} dispatch_queue_get_label(baz); } struct SelfReferencingStruct { SelfReferencingStruct* ptr; SomeObj* obj { nullptr }; }; void foo7(SomeObj* obj) { SelfReferencingStruct bar = { &bar, obj }; [bar.obj doWork]; } void foo8(SomeObj* obj) { RetainPtr foo; { SomeObj *bar = foo.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} foo = nullptr; [bar doWork]; } RetainPtr baz; { SomeObj *bar = baz.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} baz = obj; [bar doWork]; } foo = nullptr; { SomeObj *bar = foo.get(); // No warning. It's okay to mutate RefPtr in an outer scope. [bar doWork]; } foo = obj; { SomeObj *bar = foo.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} foo.clear(); [bar doWork]; } { SomeObj *bar = foo.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} foo = obj ? obj : nullptr; [bar doWork]; } { SomeObj *bar = [foo.get() other] ? foo.get() : nullptr; // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} foo = nullptr; [bar doWork]; } } void foo9(SomeObj* o) { RetainPtr guardian(o); { SomeObj *bar = guardian.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} guardian = o; // We don't detect that we're setting it to the same value. [bar doWork]; } { SomeObj *bar = guardian.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} RetainPtr other(bar); // We don't detect other has the same value as guardian. guardian.swap(other); [bar doWork]; } { SomeObj *bar = guardian.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} RetainPtr other(static_cast&&>(guardian)); [bar doWork]; } { SomeObj *bar = guardian.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} guardian.clear(); [bar doWork]; } { SomeObj *bar = guardian.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} guardian = [o other] ? o : bar; [bar doWork]; } } bool trivialFunction(CFMutableArrayRef array) { return !!array; } void foo10() { RetainPtr array = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10)); { CFMutableArrayRef arrayRef = array.get(); CFArrayAppendValue(arrayRef, nullptr); } { CFMutableArrayRef arrayRef = array.get(); // expected-warning@-1{{Local variable 'arrayRef' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} array = nullptr; CFArrayAppendValue(arrayRef, nullptr); } { CFMutableArrayRef arrayRef = array.get(); if (trivialFunction(arrayRef)) arrayRef = nullptr; } } bool trivialFunction(dispatch_queue_t queue) { return !!queue; } void foo11() { OSObjectPtr queue = adoptOSObject(dispatch_queue_create("some queue", nullptr)); { dispatch_queue_t queuePtr = queue.get(); dispatch_queue_get_label(queuePtr); } { dispatch_queue_t queuePtr = queue.get(); // expected-warning@-1{{Local variable 'queuePtr' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} queue = nullptr; dispatch_queue_get_label(queuePtr); } { dispatch_queue_t queuePtr = queue.get(); if (trivialFunction(queuePtr)) queuePtr = nullptr; } } } // namespace guardian_scopes namespace auto_keyword { class Foo { SomeObj *provide_obj(); CFMutableArrayRef provide_cf_array(); dispatch_queue_t provide_queue(); void doWork(CFMutableArrayRef); void doWork(dispatch_queue_t); void evil_func() { SomeObj *bar = provide_obj(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} auto *baz = provide_obj(); // expected-warning@-1{{Local variable 'baz' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} auto *baz2 = this->provide_obj(); // expected-warning@-1{{Local variable 'baz2' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} [[clang::suppress]] auto *baz_suppressed = provide_obj(); // no-warning } void func() { SomeObj *bar = provide_obj(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} if (bar) [bar doWork]; } void bar() { auto bar = provide_cf_array(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} doWork(bar); [[clang::suppress]] auto baz = provide_cf_array(); // no-warning doWork(baz); } void baz() { auto value1 = provide_queue(); // expected-warning@-1{{Local variable 'value1' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} doWork(value1); [[clang::suppress]] auto value2 = provide_queue(); // no-warning doWork(value2); } }; } // namespace auto_keyword namespace guardian_casts { void foo1() { RetainPtr foo; { SomeObj *bar = downcast(foo.get()); [bar doWork]; } } void foo2() { RetainPtr foo; { SomeObj *bar = static_cast(downcast(foo.get())); someFunction(); } } void foo3() { OSObjectPtr foo; { dispatch_queue_t bar = foo.get(); someFunction(); } } } // namespace guardian_casts namespace conditional_op { SomeObj *provide_obj(); bool bar(); void foo() { SomeObj *a = bar() ? nullptr : provide_obj(); // expected-warning@-1{{Local variable 'a' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} RetainPtr b = provide_obj(); { SomeObj* c = bar() ? nullptr : b.get(); [c doWork]; SomeObj* d = bar() ? b.get() : nullptr; [d doWork]; } } } // namespace conditional_op namespace local_assignment_basic { SomeObj *provide_obj(); void foo(SomeObj* a) { SomeObj* b = a; // expected-warning@-1{{Local variable 'b' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} if ([b other]) b = provide_obj(); } void bar(SomeObj* a) { SomeObj* b; // expected-warning@-1{{Local variable 'b' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} b = provide_obj(); } void baz() { RetainPtr a = provide_obj(); { SomeObj* b = a.get(); // expected-warning@-1{{Local variable 'b' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} b = provide_obj(); } } } // namespace local_assignment_basic namespace local_assignment_to_parameter { SomeObj *provide_obj(); void someFunction(); void foo(SomeObj* a) { a = provide_obj(); // expected-warning@-1{{Assignment to an unretained parameter 'a' is unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} someFunction(); [a doWork]; } CFMutableArrayRef provide_cf_array(); void doWork(CFMutableArrayRef); void bar(CFMutableArrayRef a) { a = provide_cf_array(); // expected-warning@-1{{Assignment to an unretained parameter 'a' is unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} doWork(a); } dispatch_queue_t provide_queue(); void doWork(dispatch_queue_t); void baz(dispatch_queue_t a) { a = provide_queue(); // expected-warning@-1{{Assignment to an unretained parameter 'a' is unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} doWork(a); } } // namespace local_assignment_to_parameter namespace local_assignment_to_static_local { SomeObj *provide_obj(); void someFunction(); void foo() { static SomeObj* a = nullptr; // expected-warning@-1{{Static local variable 'a' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} a = provide_obj(); someFunction(); [a doWork]; } CFMutableArrayRef provide_cf_array(); void doWork(CFMutableArrayRef); void bar() { static CFMutableArrayRef a = nullptr; // expected-warning@-1{{Static local variable 'a' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} a = provide_cf_array(); doWork(a); } dispatch_queue_t provide_queue(); void doWork(dispatch_queue_t); void baz() { static dispatch_queue_t a = nullptr; // expected-warning@-1{{Static local variable 'a' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} a = provide_queue(); doWork(a); } } // namespace local_assignment_to_static_local namespace local_assignment_to_global { SomeObj *provide_obj(); void someFunction(); SomeObj* g_a = nullptr; // expected-warning@-1{{Global variable 'local_assignment_to_global::g_a' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} void foo() { g_a = provide_obj(); someFunction(); [g_a doWork]; } CFMutableArrayRef provide_cf_array(); void doWork(CFMutableArrayRef); CFMutableArrayRef g_b = nullptr; // expected-warning@-1{{Global variable 'local_assignment_to_global::g_b' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} void bar() { g_b = provide_cf_array(); doWork(g_b); } dispatch_queue_t provide_queue(); void doWork(dispatch_queue_t); dispatch_queue_t g_c = nullptr; // expected-warning@-1{{Global variable 'local_assignment_to_global::g_c' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} void baz() { g_c = provide_queue(); doWork(g_c); } } // namespace local_assignment_to_global namespace local_var_for_singleton { SomeObj *singleton(); SomeObj *otherSingleton(); void foo() { SomeObj* bar = singleton(); SomeObj* baz = otherSingleton(); } CFMutableArrayRef cfSingleton(); void bar() { CFMutableArrayRef cf = cfSingleton(); } dispatch_queue_t osSingleton(); void baz() { dispatch_queue_t os = osSingleton(); } } namespace ptr_conversion { SomeObj *provide_obj(); void dobjc(SomeObj* obj) { if (auto *otherObj = dynamic_objc_cast(obj)) [otherObj doMoreWork:nil]; } void cobjc(SomeObj* obj) { auto *otherObj = checked_objc_cast(obj); [otherObj doMoreWork:nil]; } unsigned dcf(CFTypeRef obj) { if (CFArrayRef array = dynamic_cf_cast(obj)) return CFArrayGetCount(array); return 0; } unsigned ccf(CFTypeRef obj) { CFArrayRef array = checked_cf_cast(obj); return CFArrayGetCount(array); } } // ptr_conversion namespace const_global { extern NSString * const SomeConstant; extern CFDictionaryRef const SomeDictionary; extern dispatch_queue_t const SomeQueue; void doWork(NSString *, CFDictionaryRef, dispatch_queue_t); void use_const_global() { doWork(SomeConstant, SomeDictionary, SomeQueue); } NSString *provide_str(); CFDictionaryRef provide_dict(); dispatch_queue_t provide_queue(); void use_const_local() { NSString * const str = provide_str(); // expected-warning@-1{{Local variable 'str' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} CFDictionaryRef dict = provide_dict(); // expected-warning@-1{{Local variable 'dict' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} dispatch_queue_t queue = provide_queue(); // expected-warning@-1{{Local variable 'queue' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} doWork(str, dict, queue); } } // namespace const_global namespace ns_retained_return_value { NSString *provideNS() NS_RETURNS_RETAINED; CFDictionaryRef provideCF() CF_RETURNS_RETAINED; dispatch_queue_t provideOS() CF_RETURNS_RETAINED; void consumeNS(NSString *); void consumeCF(CFDictionaryRef); int consumeOS(dispatch_queue_t); unsigned foo() { auto *string = provideNS(); auto *dictionary = provideCF(); auto* queue = provideOS(); return string.length + CFDictionaryGetCount(dictionary) + consumeOS(queue); } } // namespace ns_retained_return_value namespace autoreleased { NSString *provideAutoreleased() __attribute__((ns_returns_autoreleased)); void consume(NSString *); void foo() { auto *string = provideAutoreleased(); consume(string); } } // autoreleased namespace ns_global { void consumeCFString(CFStringRef); void consumeNSString(NSString *); void cf() { auto *str = kCFURLTagNamesKey; consumeCFString(str); auto *localStr = LocalGlobalCFString; // expected-warning@-1{{Local variable 'localStr' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} consumeCFString(localStr); } void ns() { auto *str = NSApplicationDidBecomeActiveNotification; consumeNSString(str); auto *localStr = LocalGlobalNSString; // expected-warning@-1{{Local variable 'localStr' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} consumeNSString(localStr); } } bool doMoreWorkOpaque(OtherObj*); SomeObj* provide(); @implementation OtherObj - (instancetype)init { self = [super init]; return self; } - (void)doMoreWork:(OtherObj *)other { doMoreWorkOpaque(other); } - (SomeObj*)getSomeObj { return RetainPtr(provide()).autorelease(); } - (void)storeSomeObj { auto *obj = [self getSomeObj]; [obj doWork]; } - (void)assignToGuardianArg:(RetainPtr&)obj { SomeObj* ptr = obj.get(); // expected-warning@-1{{Local variable 'ptr' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} obj = nullptr; [ptr doWork]; } @end