Index: lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp +++ lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp @@ -37,8 +37,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include -#include +#include "llvm/ADT/StringSet.h" using namespace clang; using namespace ento; @@ -54,13 +53,31 @@ void checkASTCodeBody(const Decl *D, AnalysisManager &AM, BugReporter &BR) const; +private: + std::vector SelectorsWithAutoreleasingPool = { + "enumerateObjectsUsingBlock:", + "enumerateKeysAndObjectsUsingBlock:", + "enumerateKeysAndObjectsWithOptions:usingBlock:", + "enumerateObjectsWithOptions:usingBlock:", + "enumerateObjectsAtIndexes:options:usingBlock:", + "enumerateIndexesWithOptions:usingBlock:", + "enumerateIndexesUsingBlock:", + "enumerateIndexesInRange:options:usingBlock:", + "enumerateRangesUsingBlock:", + "enumerateRangesWithOptions:usingBlock:", + "enumerateRangesInRange:options:usingBlock:" + "objectWithOptions:passingTest:", + }; + + std::vector FunctionsWithAutoreleasingPool = { + "dispatch_async", "dispatch_group_async", "dispatch_barrier_async"}; }; } -static auto callsName(const char *FunctionName) +static auto callsNames(std::vector FunctionNames) -> decltype(callee(functionDecl())) { - return callee(functionDecl(hasName(FunctionName))); + return callee(functionDecl(hasAnyNameIn(FunctionNames))); } static void emitDiagnostics(BoundNodes &Match, @@ -99,8 +116,7 @@ hasOperatorName("*"), hasUnaryOperand( ignoringParenImpCasts( - declRefExpr(to(parmVarDecl(equalsBoundNode(ParamBind))) - ))) + declRefExpr(to(parmVarDecl(equalsBoundNode(ParamBind)))))) )), hasOperatorName("=") ).bind(ProblematicWriteBind); @@ -111,9 +127,10 @@ forEachDescendant(WritesIntoM) )); - // FIXME: use a list of APIs which can dispatch in asynchronous manner. - auto CallsAsyncM = callExpr(allOf(callsName("dispatch_async"), - WritesInBlockM)); + auto CallsAsyncM = stmt(anyOf(callExpr(allOf( + callsNames(FunctionsWithAutoreleasingPool), WritesInBlockM)), + objcMessageExpr(allOf( + hasAnySelectorIn(SelectorsWithAutoreleasingPool), WritesInBlockM)))); auto DoublePointerParamM = parmVarDecl(hasType(pointerType( Index: test/Analysis/autoreleasewritechecker_test.m =================================================================== --- test/Analysis/autoreleasewritechecker_test.m +++ test/Analysis/autoreleasewritechecker_test.m @@ -18,11 +18,19 @@ typedef int dispatch_semaphore_t; typedef void (^block_t)(); -typedef NSError *__autoreleasing* MYERROR; +@interface NSArray +- (void) enumerateObjectsUsingBlock:(block_t)block; +@end + +typedef int group_t; typedef struct dispatch_queue_s *dispatch_queue_t; typedef void (^dispatch_block_t)(void); extern dispatch_queue_t queue; + +void dispatch_group_async(dispatch_queue_t queue, + group_t group, + dispatch_block_t block); void dispatch_async(dispatch_queue_t queue, dispatch_block_t block); dispatch_semaphore_t dispatch_semaphore_create(int); @@ -35,6 +43,7 @@ - (BOOL) writeToLocalErrorInBlock:(NSError **)error; - (BOOL) writeToErrorInBlockMultipleTimes:(NSError *__autoreleasing *)error; - (BOOL) writeToError:(NSError *__autoreleasing *)error; +- (BOOL) writeToErrorWithDispatchGroup:(NSError *__autoreleasing *)error; @end @implementation I @@ -52,6 +61,19 @@ return 0; } +- (BOOL) writeToErrorWithDispatchGroup:(NSError *__autoreleasing *)error { + dispatch_semaphore_t sem = dispatch_semaphore_create(0l); + dispatch_group_async(queue, 0, ^{ + if (error) { + *error = [NSError errorWithDomain:1]; // expected-warning{{Writing into an auto-releasing variable}} + } + dispatch_semaphore_signal(sem); + }); + + dispatch_semaphore_wait(sem, 100); + return 0; +} + - (BOOL) writeToLocalErrorInBlock:(NSError *__autoreleasing *)error { dispatch_semaphore_t sem = dispatch_semaphore_create(0l); dispatch_async(queue, ^{ @@ -124,3 +146,10 @@ *error = [NSError errorWithDomain:1]; // no-warning return 0; } + +BOOL writeToErrorWithIterator(NSError *__autoreleasing* error, NSArray *a) { + [a enumerateObjectsUsingBlock:^{ + *error = [NSError errorWithDomain:1]; // expected-warning{{Writing into an auto-releasing variable}} + }]; + return 0; +}