Index: lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp +++ lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp @@ -115,14 +115,16 @@ QualType Ty = PVD->getType(); if (Ty->getPointeeType().getObjCLifetime() != Qualifiers::OCL_Autoreleasing) return; - const char *WarningMsg = "Write to"; + const char *ActionMsg = "Write to"; const auto *MarkedStmt = Match.getNodeAs(ProblematicWriteBind); + bool IsCapture = false; // Prefer to warn on write, but if not available, warn on capture. if (!MarkedStmt) { MarkedStmt = Match.getNodeAs(CapturedBind); assert(MarkedStmt); - WarningMsg = "Capture of"; + ActionMsg = "Capture of"; + IsCapture = true; } SourceRange Range = MarkedStmt->getSourceRange(); @@ -130,12 +132,14 @@ MarkedStmt, BR.getSourceManager(), ADC); bool IsMethod = Match.getNodeAs(IsMethodBind) != nullptr; const char *Name = IsMethod ? "method" : "function"; + BR.EmitBasicReport( ADC->getDecl(), Checker, - /*Name=*/(llvm::Twine(WarningMsg) + /*Name=*/(llvm::Twine(ActionMsg) + " autoreleasing out parameter inside autorelease pool").str(), /*Category=*/"Memory", - (llvm::Twine(WarningMsg) + " autoreleasing out parameter inside " + + (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") .str(), @@ -153,7 +157,7 @@ .bind(ParamBind); auto ReferencedParamM = - declRefExpr(to(parmVarDecl(DoublePointerParamM))); + declRefExpr(to(parmVarDecl(DoublePointerParamM))).bind(CapturedBind); // Write into a binded object, e.g. *ParamBind = X. auto WritesIntoM = binaryOperator( @@ -169,7 +173,7 @@ ignoringParenImpCasts(ReferencedParamM)); auto CapturedInParamM = stmt(anyOf( callExpr(ArgumentCaptureM), - objcMessageExpr(ArgumentCaptureM))).bind(CapturedBind); + objcMessageExpr(ArgumentCaptureM))); // WritesIntoM happens inside a block passed as an argument. auto WritesOrCapturesInBlockM = hasAnyArgument(allOf( @@ -192,7 +196,8 @@ auto MatcherM = decl(anyOf( objcMethodDecl(HasParamAndWritesInMarkedFuncM).bind(IsMethodBind), - functionDecl(HasParamAndWritesInMarkedFuncM))); + functionDecl(HasParamAndWritesInMarkedFuncM), + blockDecl(HasParamAndWritesInMarkedFuncM))); auto Matches = match(MatcherM, *D, AM.getASTContext()); for (BoundNodes Match : Matches) Index: test/Analysis/autoreleasewritechecker_test.m =================================================================== --- test/Analysis/autoreleasewritechecker_test.m +++ test/Analysis/autoreleasewritechecker_test.m @@ -265,5 +265,17 @@ }]; } +typedef void (^errBlock)(NSError *__autoreleasing *error); + +extern void expectError(errBlock); + +void captureAutoreleasingVarFromBlock(NSDictionary *dict) { + expectError(^(NSError *__autoreleasing *err) { + [dict enumerateKeysAndObjectsUsingBlock:^{ + writeIntoError(err); // expected-warning{{Capture of autoreleasing out parameter 'err'}} + }]; + }); +} + #endif