Author: Paul Pelzl Date: 2020-06-03T19:06:04+03:00 New Revision: 7113271528a4c6efc8b57f25ead28f65b5e48757
URL: https://github.com/llvm/llvm-project/commit/7113271528a4c6efc8b57f25ead28f65b5e48757 DIFF: https://github.com/llvm/llvm-project/commit/7113271528a4c6efc8b57f25ead28f65b5e48757.diff LOG: [analyzer] ObjCAutoreleaseWriteChecker: Support explicit autoreleasepools. The checker currently supports only a whitelist of block-enumeration methods which are known to internally clear an autorelease pool. Extend this checker to detect writes within the scope of explicit @autoreleasepool statements. rdar://25301111 Differential Revision: https://reviews.llvm.org/D81072 Added: Modified: clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp clang/test/Analysis/autoreleasewritechecker_test.m Removed: ################################################################################ diff --git a/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp index f36e971bb8ba..7fd6e2abef4c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp @@ -30,6 +30,7 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/Twine.h" @@ -44,6 +45,7 @@ const char *ProblematicWriteBind = "problematicwrite"; const char *CapturedBind = "capturedbind"; const char *ParamBind = "parambind"; const char *IsMethodBind = "ismethodbind"; +const char *IsARPBind = "isautoreleasepoolbind"; class ObjCAutoreleaseWriteChecker : public Checker<check::ASTCodeBody> { public: @@ -128,21 +130,39 @@ static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR, SourceRange Range = MarkedStmt->getSourceRange(); PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin( MarkedStmt, BR.getSourceManager(), ADC); + bool IsMethod = Match.getNodeAs<ObjCMethodDecl>(IsMethodBind) != nullptr; - const char *Name = IsMethod ? "method" : "function"; - - BR.EmitBasicReport( - ADC->getDecl(), Checker, - /*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") - .str(), - Location, - Range); + const char *FunctionDescription = IsMethod ? "method" : "function"; + bool IsARP = Match.getNodeAs<ObjCAutoreleasePoolStmt>(IsARPBind) != nullptr; + + llvm::SmallString<128> BugNameBuf; + llvm::raw_svector_ostream BugName(BugNameBuf); + BugName << ActionMsg + << " autoreleasing out parameter inside autorelease pool"; + + llvm::SmallString<128> BugMessageBuf; + llvm::raw_svector_ostream BugMessage(BugMessageBuf); + BugMessage << ActionMsg << " autoreleasing out parameter "; + if (IsCapture) + BugMessage << "'" + PVD->getName() + "' "; + + BugMessage << "inside "; + if (IsARP) + BugMessage << "locally-scoped autorelease pool;"; + else + BugMessage << "autorelease pool that may exit before " + << FunctionDescription << " returns;"; + + BugMessage << " consider writing first to a strong local variable" + " declared outside "; + if (IsARP) + BugMessage << "of the autorelease pool"; + else + BugMessage << "of the block"; + + BR.EmitBasicReport(ADC->getDecl(), Checker, BugName.str(), + categories::MemoryRefCount, BugMessage.str(), Location, + Range); } void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D, @@ -188,9 +208,16 @@ void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D, 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), diff --git a/clang/test/Analysis/autoreleasewritechecker_test.m b/clang/test/Analysis/autoreleasewritechecker_test.m index b3d34b9b11fa..8eab348193d7 100644 --- a/clang/test/Analysis/autoreleasewritechecker_test.m +++ b/clang/test/Analysis/autoreleasewritechecker_test.m @@ -83,6 +83,10 @@ - (BOOL) writeToLocalErrorInBlock:(NSError **)error; - (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 @@ - (BOOL) writeToErrorInBlockMultipleTimes:(NSError *__autoreleasing *)error { 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 @@ BOOL writeToErrorInBlockFromCFunc(NSError *__autoreleasing* error) { 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; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits