Index: clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp @@ -44,6 +44,7 @@ const char *CapturedBind = "capturedbind"; const char *ParamBind = "parambind"; const char *IsMethodBind = "ismethodbind"; +const char *IsARPBind = "isautoreleasepoolbind"; class ObjCAutoreleaseWriteChecker : public Checker { public: @@ -130,19 +131,24 @@ MarkedStmt, BR.getSourceManager(), ADC); bool IsMethod = Match.getNodeAs(IsMethodBind) != nullptr; const char *Name = IsMethod ? "method" : "function"; + bool IsARP = Match.getNodeAs(IsARPBind) != nullptr; BR.EmitBasicReport( ADC->getDecl(), Checker, - /*Name=*/(llvm::Twine(ActionMsg) - + " autoreleasing out parameter inside autorelease pool").str(), + /*Name=*/ + (llvm::Twine(ActionMsg) + + " autoreleasing out parameter inside autorelease pool") + .str(), /*BugCategory=*/"Memory", (llvm::Twine(ActionMsg) + " autoreleasing out parameter " + (IsCapture ? "'" + PVD->getName() + "'" + " " : "") + "inside " + - "autorelease pool that may exit before " + Name + " returns; consider " - "writing first to a strong local variable declared outside of the block") + (IsARP ? "locally-scoped autorelease pool;" + : std::string("autorelease pool that may exit before ") + Name + + " returns;") + + " consider writing first to a strong local variable declared outside " + + (IsARP ? "of the autorelease pool" : "of the block")) .str(), - Location, - Range); + Location, Range); } void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D, @@ -188,9 +194,16 @@ WritesOrCapturesInBlockM)) )); - auto HasParamAndWritesInMarkedFuncM = allOf( - hasAnyParameter(DoublePointerParamM), - forEachDescendant(BlockPassedToMarkedFuncM)); + // WritesIntoM happens inside an explicit @autoreleasepool. + auto WritesOrCapturesInPoolM = + autoreleasePoolStmt( + forEachDescendant(stmt(anyOf(WritesIntoM, CapturedInParamM)))) + .bind(IsARPBind); + + auto HasParamAndWritesInMarkedFuncM = + allOf(hasAnyParameter(DoublePointerParamM), + anyOf(forEachDescendant(BlockPassedToMarkedFuncM), + forEachDescendant(WritesOrCapturesInPoolM))); auto MatcherM = decl(anyOf( objcMethodDecl(HasParamAndWritesInMarkedFuncM).bind(IsMethodBind), Index: clang/test/Analysis/autoreleasewritechecker_test.m =================================================================== --- clang/test/Analysis/autoreleasewritechecker_test.m +++ clang/test/Analysis/autoreleasewritechecker_test.m @@ -83,6 +83,10 @@ - (BOOL) writeToErrorInBlockMultipleTimes:(NSError *__autoreleasing *)error; - (BOOL) writeToError:(NSError *__autoreleasing *)error; - (BOOL) writeToErrorWithDispatchGroup:(NSError *__autoreleasing *)error; +- (BOOL)writeToErrorInAutoreleasePool:(NSError *__autoreleasing *)error; +- (BOOL)writeToStrongErrorInAutoreleasePool:(NSError *__strong *)error; +- (BOOL)writeToLocalErrorInAutoreleasePool:(NSError *__autoreleasing *)error; +- (BOOL)writeToErrorInAutoreleasePoolMultipleTimes:(NSError *__autoreleasing *)error; @end @implementation I @@ -162,6 +166,58 @@ return 0; } +- (BOOL)writeToErrorInAutoreleasePool:(NSError *__autoreleasing *)error { + @autoreleasepool { + if (error) { + *error = [NSError errorWithDomain:1]; // expected-warning{{Write to autoreleasing out parameter inside locally-scoped autorelease pool; consider writing first to a strong local variable declared outside of the autorelease pool}} + } + } + + return 0; +} + +- (BOOL)writeToStrongErrorInAutoreleasePool:(NSError *__strong *)error { + @autoreleasepool { + if (error) { + *error = [NSError errorWithDomain:1]; // no-warning + } + } + + return 0; +} + +- (BOOL)writeToLocalErrorInAutoreleasePool:(NSError *__autoreleasing *)error { + NSError *localError; + @autoreleasepool { + localError = [NSError errorWithDomain:1]; // no-warning + } + + if (error) { + *error = localError; // no-warning + } + + return 0; +} + +- (BOOL)writeToErrorInAutoreleasePoolMultipleTimes:(NSError *__autoreleasing *)error { + @autoreleasepool { + if (error) { + *error = [NSError errorWithDomain:1]; // expected-warning{{Write to autoreleasing out parameter inside locally-scoped autorelease pool; consider writing first to a strong local variable declared outside of the autorelease pool}} + } + } + if (error) { + *error = [NSError errorWithDomain:1]; // no-warning + } + @autoreleasepool { + if (error) { + *error = [NSError errorWithDomain:1]; // expected-warning{{Write to autoreleasing out parameter inside locally-scoped autorelease pool; consider writing first to a strong local variable declared outside of the autorelease pool}} + *error = [NSError errorWithDomain:1]; // expected-warning{{Write to autoreleasing out parameter inside locally-scoped autorelease pool; consider writing first to a strong local variable declared outside of the autorelease pool}} + } + } + + return 0; +} + - (BOOL) writeToError:(NSError *__autoreleasing *)error { *error = [NSError errorWithDomain:1]; // no-warning return 0; @@ -181,6 +237,15 @@ return 0; } +BOOL writeIntoErrorInAutoreleasePoolFromCFunc(NSError *__autoreleasing *error) { + @autoreleasepool { + if (error) { + *error = [NSError errorWithDomain:1]; // expected-warning{{Write to autoreleasing out parameter inside locally-scoped autorelease pool; consider writing first to a strong local variable declared outside of the autorelease pool}} + } + } + return 0; +} + BOOL writeToErrorNoWarning(NSError *__autoreleasing* error) { *error = [NSError errorWithDomain:1]; // no-warning return 0;