[clang] 7113271 - [analyzer] ObjCAutoreleaseWriteChecker: Support explicit autoreleasepools.
Artem Dergachev via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 3 09:06:20 PDT 2020
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;
More information about the cfe-commits
mailing list