From 12b9434d5bf801e24242b1f6fd04899f8a7fa92c Mon Sep 17 00:00:00 2001
From: Ted Kremenek
Date: Thu, 27 Jan 2011 06:54:14 +0000
Subject: [PATCH] Hook up attribute ns_consumes_self in the ObjC retain/release
checker in the static analyzer.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124360 91177308-0d34-0410-b5e6-96231b3b80d8
---
lib/Lex/PPMacroExpansion.cpp | 1 +
lib/StaticAnalyzer/CFRefCount.cpp | 9 +++++++++
test/Analysis/retain-release.m | 19 +++++++++++++++++-
www/analyzer/annotations.html | 33 +++++++++++++++++++++++++++++++
4 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp
index 7727eafd73..c7233344f4 100644
--- a/lib/Lex/PPMacroExpansion.cpp
+++ b/lib/Lex/PPMacroExpansion.cpp
@@ -536,6 +536,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
.Case("attribute_ext_vector_type", true)
.Case("attribute_ns_returns_not_retained", true)
.Case("attribute_ns_returns_retained", true)
+ .Case("attribute_ns_consumes_self", true)
.Case("attribute_objc_ivar_unused", true)
.Case("attribute_overloadable", true)
.Case("attribute_unavailable_with_message", true)
diff --git a/lib/StaticAnalyzer/CFRefCount.cpp b/lib/StaticAnalyzer/CFRefCount.cpp
index 473b7313bb..546687a7a2 100644
--- a/lib/StaticAnalyzer/CFRefCount.cpp
+++ b/lib/StaticAnalyzer/CFRefCount.cpp
@@ -467,6 +467,10 @@ public:
/// terminate the path.
bool isEndPath() const { return EndPath; }
+
+ /// Sets the effect on the receiver of the message.
+ void setReceiverEffect(ArgEffect e) { Receiver = e; }
+
/// getReceiverEffect - Returns the effect on the receiver of the call.
/// This is only meaningful if the summary applies to an ObjCMessageExpr*.
ArgEffect getReceiverEffect() const { return Receiver; }
@@ -1219,6 +1223,11 @@ RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ,
bool isTrackedLoc = false;
+ // Effects on the receiver.
+ if (MD->getAttr()) {
+ Summ.setReceiverEffect(DecRefMsg);
+ }
+
// Determine if there is a special return effect for this method.
if (cocoa::isCocoaObjectRef(MD->getResultType())) {
if (MD->getAttr()) {
diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m
index eefd132282..e68e4d7e75 100644
--- a/test/Analysis/retain-release.m
+++ b/test/Analysis/retain-release.m
@@ -13,6 +13,9 @@
#if __has_feature(attribute_cf_returns_not_retained)
#define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained))
#endif
+#if __has_feature(attribute_ns_consumes_self)
+#define NS_CONSUMES_SELF __attribute__((ns_consumes_self))
+#endif
//===----------------------------------------------------------------------===//
// The following code is reduced using delta-debugging from Mac OS X headers:
@@ -1211,6 +1214,7 @@ typedef NSString* MyStringTy;
- (NSString*) newString NS_RETURNS_NOT_RETAINED; // no-warning
- (NSString*) newStringNoAttr;
- (int) returnsAnOwnedInt NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to methods that return an Objective-C object}}
+- (id) pseudoInit NS_CONSUMES_SELF NS_RETURNS_RETAINED;
@end
static int ownership_attribute_doesnt_go_here NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to functions and methods}}
@@ -1228,6 +1232,19 @@ void test_attr1c(TestOwnershipAttr *X) {
NSString *str2 = [X newStringNoAttr]; // expected-warning{{leak}}
}
+void testattr2_a() {
+ TestOwnershipAttr *x = [TestOwnershipAttr alloc]; // expected-warning{{leak}}
+}
+
+void testattr2_b() {
+ TestOwnershipAttr *x = [[TestOwnershipAttr alloc] pseudoInit]; // expected-warning{{leak}}
+}
+
+void testattr2_c() {
+ TestOwnershipAttr *x = [[TestOwnershipAttr alloc] pseudoInit]; // no-warning
+ [x release];
+}
+
@interface MyClassTestCFAttr : NSObject {}
- (NSDate*) returnsCFRetained CF_RETURNS_RETAINED;
- (CFDateRef) returnsCFRetainedAsCF CF_RETURNS_RETAINED;
@@ -1401,7 +1418,7 @@ static void rdar_8724287(CFErrorRef error)
while (error_to_dump != ((void*)0)) {
CFDictionaryRef info;
- info = CFErrorCopyUserInfo(error_to_dump); // expected-warning{{Potential leak of an object allocated on line 1404 and stored into 'info'}}
+ info = CFErrorCopyUserInfo(error_to_dump); // expected-warning{{Potential leak of an object allocated on line 1421 and stored into 'info'}}
if (info != ((void*)0)) {
}
diff --git a/www/analyzer/annotations.html b/www/analyzer/annotations.html
index f43e361cca..b2182eb934 100644
--- a/www/analyzer/annotations.html
+++ b/www/analyzer/annotations.html
@@ -56,6 +56,7 @@ recognized by GCC. Their use can be conditioned using preprocessor macros
Attribute 'ns_returns_not_retained'
Attribute 'cf_returns_retained'
Attribute 'cf_returns_not_retained'
+ Attribute 'ns_consumes_self'
@@ -350,6 +351,38 @@ its availability, as it is not available in earlier versions of the analyzer:
+Attribute 'ns_consumes_self'
+(Clang-specific)
+
+The 'ns_consumes_self' attribute can be placed only on an Objective-C method declaration.
+ It indicates that the receiver of the message is "consumed" (a single reference count decremented)
+ after the message is sent. This matches the semantics of all "init" methods.
+
+
+One use of this attribute is declare your own init-like methods that do not follow the
+ standard Cocoa naming conventions. For example:
+
+
+#ifndef __has_feature
+#define __has_Feature(x) 0 // Compatibility with non-clang compilers.
+#endif
+
+#ifndef NS_CONSUMES_SELF
+#if __has_feature((attribute_ns_consumes_self))
+#else
+#define NS_CONSUMES_SELF
+#endif
+#endif
+
+@interface MyClass : NSObject
+- initWith:(MyClass *)x;
+- nonstandardInitWith:(MyClass *)x NS_CONSUMES_SELF NS_RETURNS_RETAINED;
+@end
+
+
+In this example, nonstandardInitWith: has the same ownership semantics as the init method initWith:.
+ The static analyzer will observe that the method consumes the receiver, and then returns an object with a +1 retain count.
+
Custom Assertion Handlers
--
2.50.1