diff options
| author | Ted Kremenek <kremenek@apple.com> | 2011-01-27 18:43:03 +0000 |
|---|---|---|
| committer | Ted Kremenek <kremenek@apple.com> | 2011-01-27 18:43:03 +0000 |
| commit | afe348ea4361b6f225f3bd87ddd9c75c4585f891 (patch) | |
| tree | 011e97d60d251fdecd1898af4ebc97ae4b9a80c9 /clang | |
| parent | 9c2e896d6245e1eebeccfeec668d2afbb46fae1b (diff) | |
| download | bcm5719-llvm-afe348ea4361b6f225f3bd87ddd9c75c4585f891.tar.gz bcm5719-llvm-afe348ea4361b6f225f3bd87ddd9c75c4585f891.zip | |
Wire up attributes 'ns_consumed' and 'cf_consumed' in the static analyzer's ObjC retain/release checker.
llvm-svn: 124386
Diffstat (limited to 'clang')
| -rw-r--r-- | clang/lib/Lex/PPMacroExpansion.cpp | 2 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/CFRefCount.cpp | 35 | ||||
| -rw-r--r-- | clang/test/Analysis/retain-release.m | 28 | ||||
| -rw-r--r-- | clang/www/analyzer/annotations.html | 121 |
4 files changed, 178 insertions, 8 deletions
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index c7233344f44..5fb55159de2 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -537,6 +537,8 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("attribute_ns_returns_not_retained", true) .Case("attribute_ns_returns_retained", true) .Case("attribute_ns_consumes_self", true) + .Case("attribute_ns_consumed", true) + .Case("attribute_cf_consumed", true) .Case("attribute_objc_ivar_unused", true) .Case("attribute_overloadable", true) .Case("attribute_unavailable_with_message", true) diff --git a/clang/lib/StaticAnalyzer/CFRefCount.cpp b/clang/lib/StaticAnalyzer/CFRefCount.cpp index 546687a7a2c..3278c4a2bfa 100644 --- a/clang/lib/StaticAnalyzer/CFRefCount.cpp +++ b/clang/lib/StaticAnalyzer/CFRefCount.cpp @@ -451,6 +451,10 @@ public: return DefaultArgEffect; } + + void addArg(ArgEffects::Factory &af, unsigned idx, ArgEffect e) { + Args = af.add(Args, idx, e); + } /// setDefaultArgEffect - Set the default argument effect. void setDefaultArgEffect(ArgEffect E) { @@ -1191,6 +1195,20 @@ RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, if (!FD) return; + // Effects on the parameters. + unsigned parm_idx = 0; + for (FunctionDecl::param_const_iterator pi = FD->param_begin(), + pe = FD->param_end(); pi != pe; ++pi) { + const ParmVarDecl *pd = *pi; + if (pd->getAttr<NSConsumedAttr>()) { + if (!GCEnabled) + Summ.addArg(AF, parm_idx, DecRef); + } + else if(pd->getAttr<CFConsumedAttr>()) { + Summ.addArg(AF, parm_idx, DecRef); + } + } + QualType RetTy = FD->getResultType(); // Determine if there is a special return effect for this method. @@ -1225,7 +1243,22 @@ RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, // Effects on the receiver. if (MD->getAttr<NSConsumesSelfAttr>()) { - Summ.setReceiverEffect(DecRefMsg); + if (!GCEnabled) + Summ.setReceiverEffect(DecRefMsg); + } + + // Effects on the parameters. + unsigned parm_idx = 0; + for (ObjCMethodDecl::param_iterator pi=MD->param_begin(), pe=MD->param_end(); + pi != pe; ++pi, ++parm_idx) { + const ParmVarDecl *pd = *pi; + if (pd->getAttr<NSConsumedAttr>()) { + if (!GCEnabled) + Summ.addArg(AF, parm_idx, DecRef); + } + else if(pd->getAttr<CFConsumedAttr>()) { + Summ.addArg(AF, parm_idx, DecRef); + } } // Determine if there is a special return effect for this method. diff --git a/clang/test/Analysis/retain-release.m b/clang/test/Analysis/retain-release.m index e68e4d7e759..09a633631ae 100644 --- a/clang/test/Analysis/retain-release.m +++ b/clang/test/Analysis/retain-release.m @@ -16,6 +16,12 @@ #if __has_feature(attribute_ns_consumes_self) #define NS_CONSUMES_SELF __attribute__((ns_consumes_self)) #endif +#if __has_feature(attribute_ns_consumed) +#define NS_CONSUMED __attribute__((ns_consumed)) +#endif +#if __has_feature(attribute_cf_consumed) +#define CF_CONSUMED __attribute__((cf_consumed)) +#endif //===----------------------------------------------------------------------===// // The following code is reduced using delta-debugging from Mac OS X headers: @@ -1215,6 +1221,8 @@ typedef NSString* MyStringTy; - (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; ++ (void) consume:(id) NS_CONSUMED x; ++ (void) consume2:(id) CF_CONSUMED x; @end static int ownership_attribute_doesnt_go_here NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to functions and methods}} @@ -1245,6 +1253,24 @@ void testattr2_c() { [x release]; } +void testattr3() { + TestOwnershipAttr *x = [TestOwnershipAttr alloc]; // no-warning + [TestOwnershipAttr consume:x]; + TestOwnershipAttr *y = [TestOwnershipAttr alloc]; // no-warning + [TestOwnershipAttr consume2:y]; +} + +void consume_ns(id NS_CONSUMED x); +void consume_cf(id CF_CONSUMED x); + +void testattr4() { + TestOwnershipAttr *x = [TestOwnershipAttr alloc]; // no-warning + consume_ns(x); + TestOwnershipAttr *y = [TestOwnershipAttr alloc]; // no-warning + consume_cf(y); +} + + @interface MyClassTestCFAttr : NSObject {} - (NSDate*) returnsCFRetained CF_RETURNS_RETAINED; - (CFDateRef) returnsCFRetainedAsCF CF_RETURNS_RETAINED; @@ -1418,7 +1444,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 1421 and stored into 'info'}} + info = CFErrorCopyUserInfo(error_to_dump); // expected-warning{{Potential leak of an object allocated on line 1447 and stored into 'info'}} if (info != ((void*)0)) { } diff --git a/clang/www/analyzer/annotations.html b/clang/www/analyzer/annotations.html index 4308fa4ffc3..757209f29ea 100644 --- a/clang/www/analyzer/annotations.html +++ b/clang/www/analyzer/annotations.html @@ -6,7 +6,6 @@ <link type="text/css" rel="stylesheet" href="menu.css" /> <link type="text/css" rel="stylesheet" href="content.css" /> <script type="text/javascript" src="scripts/menu.js"></script> - <script type="text/javascript" src="scripts/dbtree.js"></script> </head> <body> @@ -38,7 +37,7 @@ recognized by GCC. Their use can be conditioned using preprocessor macros <h4>Specific Topics</h4> -<ul id="collapsetree" class="dbtree onclick multiple"> +<ul> <li><a href="#generic">Annotations to Enhance Generic Checks</a> <ul> <li><a href="#null_checking"><span>Null Pointer Checking</span></a> @@ -56,6 +55,8 @@ recognized by GCC. Their use can be conditioned using preprocessor macros <li><a href="#attr_ns_returns_not_retained">Attribute 'ns_returns_not_retained'</a></li> <li><a href="#attr_cf_returns_retained">Attribute 'cf_returns_retained'</a></li> <li><a href="#attr_cf_returns_not_retained">Attribute 'cf_returns_not_retained'</a></li> + <li><a href="#attr_ns_consumed">Attribute 'ns_consumed'</a></li> + <li><a href="#attr_cf_consumed">Attribute 'cf_consumed'</a></li> <li><a href="#attr_ns_consumes_self">Attribute 'ns_consumes_self'</a></li> </ul> </li> @@ -295,15 +296,15 @@ CFDateRef returnsRetainedCFDate() { @implementation MyClass - (NSDate*) returnsCFRetained { - return (NSDate*) returnsRetainedCFDate(); // No leak. + return (NSDate*) returnsRetainedCFDate(); <b><i>// No leak.</i></b> } - (NSDate*) alsoReturnsRetained { - return (NSDate*) returnsRetainedCFDate(); // Always report a leak. + return (NSDate*) returnsRetainedCFDate(); <b><i>// Always report a leak.</i></b> } - (NSDate*) returnsNSRetained { - return (NSDate*) returnsRetainedCFDate(); // Report a leak when using GC. + return (NSDate*) returnsRetainedCFDate(); <b><i>// Report a leak when using GC.</i></b> } @end </pre> @@ -351,6 +352,112 @@ its availability, as it is not available in earlier versions of the analyzer:</p #endif </pre> +<h4 id="attr_ns_consumed">Attribute 'ns_consumed' +(Clang-specific)</h4> + +<p>The 'ns_consumed' attribute can be placed on a specific parameter in either the declaration of a function or an Objective-C method. + It indicates to the static analyzer that a <tt>release</tt> message is implicitly sent to the parameter upon + completion of the call to the given function or method. + +<p><b>Important note when using Garbage Collection</b>: Note that the analyzer +essentially ignores this attribute when code is compiled to use Objective-C +garbage collection. This is because the <tt>release</tt> message does nothing +when using GC. If the underlying function/method uses something like +<tt>CFRelease</tt> to decrement the reference count, consider using +the <a href="#attr_cf_consumed">cf_consumed</a> attribute instead.</p> + +<p><b>Example</b></p> + +<pre class="code_example"> +<span class="command">$ cat test.m</span> +#ifndef __has_feature // Optional. +#define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +#ifndef NS_CONSUMED +#if __has_feature(attribute_ns_consumed) +<span class="code_highlight">#define NS_CONSUMED __attribute__((NS_CONSUMED))</span> +#else +#define NS_CONSUMED +#endif +#endif + +void consume_ns(id <span class="code_highlight">NS_CONSUMED</span> x); + +void test() { + id x = [[NSObject alloc] init]; + consume_ns(x); <b><i>// No leak!</i></b> +} + +@interface Foo : NSObject ++ (void) releaseArg:(id) <span class="code_highlight">NS_CONSUMED</span> x; ++ (void) releaseSecondArg:(id)x second:(id) <span class="code_highlight">NS_CONSUMED</span> y; +@end + +void test_method() { + id x = [[NSObject alloc] init]; + [Foo releaseArg:x]; <b><i>// No leak!</i></b> +} + +void test_method2() { + id a = [[NSObject alloc] init]; + id b = [[NSObject alloc] init]; + [Foo releaseSecondArg:a second:b]; <b><i>// 'a' is leaked, but 'b' is released.</i></b> +} +</pre> + +<h4 id="attr_cf_consumed">Attribute 'cf_consumed' +(Clang-specific)</h4> + +<p>The 'cf_consumed' attribute is practically identical to <a href="#attr_ns_consumed">ns_consumed</a>. +The attribute can be placed on a specific parameter in either the declaration of a function or an Objective-C method. +It indicates to the static analyzer that the object reference is implicitly passed to a call to <tt>CFRelease</tt> upon +completion of the call to the given function or method.</p> + +<p>Operationally this attribute is nearly identical to ns_consumed +with the main difference that the reference count decrement still occurs when using Objective-C garbage +collection (which is import for Core Foundation types, which are not automatically garbage collected).</p> + +<p><b>Example</b></p> + +<pre class="code_example"> +<span class="command">$ cat test.m</span> +#ifndef __has_feature // Optional. +#define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +#ifndef CF_CONSUMED +#if __has_feature(attribute_cf_consumed) +<span class="code_highlight">#define CF_CONSUMED __attribute__((CF_CONSUMED))</span> +#else +#define CF_CONSUMED +#endif +#endif + +void consume_cf(id <span class="code_highlight">CF_CONSUMED</span> x); +void consume_CFDate(CFDateRef <span class="code_highlight">CF_CONSUMED</span> x); + +void test() { + id x = [[NSObject alloc] init]; + consume_cf(x); <b><i>// No leak!</i></b> +} + +void test2() { + CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); + consume_CFDate(date); <b><i>// No leak, including under GC!</i></b> + +} + +@interface Foo : NSObject ++ (void) releaseArg:(CFDateRef) <span class="code_highlight">CF_CONSUMED</span> x; +@end + +void test_method() { + CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); + [Foo releaseArg:date]; <b><i>// No leak!</i></b> +} +</pre> + <h4 id="attr_ns_consumes_self">Attribute 'ns_consumes_self' (Clang-specific)</h4> @@ -360,7 +467,9 @@ its availability, as it is not available in earlier versions of the analyzer:</p </p> <p>One use of this attribute is declare your own init-like methods that do not follow the - standard Cocoa naming conventions. For example:</p> + standard Cocoa naming conventions.</p> + +<p><b>Example</b></p> <pre class="code_example"> #ifndef __has_feature |

