From 11fe175346242744bd9cb4040f3c84e243934134 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 27 Jan 2011 18:43:03 +0000 Subject: [PATCH] Wire up attributes 'ns_consumed' and 'cf_consumed' in the static analyzer's ObjC retain/release checker. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124386 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Lex/PPMacroExpansion.cpp | 2 + lib/StaticAnalyzer/CFRefCount.cpp | 35 ++++++++- test/Analysis/retain-release.m | 28 ++++++- www/analyzer/annotations.html | 121 ++++++++++++++++++++++++++++-- 4 files changed, 178 insertions(+), 8 deletions(-) diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index c7233344f4..5fb55159de 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/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/lib/StaticAnalyzer/CFRefCount.cpp b/lib/StaticAnalyzer/CFRefCount.cpp index 546687a7a2..3278c4a2bf 100644 --- a/lib/StaticAnalyzer/CFRefCount.cpp +++ b/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()) { + if (!GCEnabled) + Summ.addArg(AF, parm_idx, DecRef); + } + else if(pd->getAttr()) { + 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()) { - 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()) { + if (!GCEnabled) + Summ.addArg(AF, parm_idx, DecRef); + } + else if(pd->getAttr()) { + Summ.addArg(AF, parm_idx, DecRef); + } } // Determine if there is a special return effect for this method. diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m index e68e4d7e75..09a633631a 100644 --- a/test/Analysis/retain-release.m +++ b/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/www/analyzer/annotations.html b/www/analyzer/annotations.html index 4308fa4ffc..757209f29e 100644 --- a/www/analyzer/annotations.html +++ b/www/analyzer/annotations.html @@ -6,7 +6,6 @@ - @@ -38,7 +37,7 @@ recognized by GCC. Their use can be conditioned using preprocessor macros

Specific Topics

-
    +
    • Annotations to Enhance Generic Checks
    • @@ -295,15 +296,15 @@ CFDateRef returnsRetainedCFDate() { @implementation MyClass - (NSDate*) returnsCFRetained { - return (NSDate*) returnsRetainedCFDate(); // No leak. + return (NSDate*) returnsRetainedCFDate(); // No leak. } - (NSDate*) alsoReturnsRetained { - return (NSDate*) returnsRetainedCFDate(); // Always report a leak. + return (NSDate*) returnsRetainedCFDate(); // Always report a leak. } - (NSDate*) returnsNSRetained { - return (NSDate*) returnsRetainedCFDate(); // Report a leak when using GC. + return (NSDate*) returnsRetainedCFDate(); // Report a leak when using GC. } @end @@ -351,6 +352,112 @@ its availability, as it is not available in earlier versions of the analyzer:

      +

      Attribute 'ns_consumed' +(Clang-specific)

      + +

      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 release message is implicitly sent to the parameter upon + completion of the call to the given function or method. + +

      Important note when using Garbage Collection: Note that the analyzer +essentially ignores this attribute when code is compiled to use Objective-C +garbage collection. This is because the release message does nothing +when using GC. If the underlying function/method uses something like +CFRelease to decrement the reference count, consider using +the cf_consumed attribute instead.

      + +

      Example

      + +
      +$ cat test.m
      +#ifndef __has_feature      // Optional.
      +#define __has_feature(x) 0 // Compatibility with non-clang compilers.
      +#endif
      +
      +#ifndef NS_CONSUMED
      +#if __has_feature(attribute_ns_consumed)
      +#define NS_CONSUMED __attribute__((NS_CONSUMED))
      +#else
      +#define NS_CONSUMED
      +#endif
      +#endif
      +
      +void consume_ns(id NS_CONSUMED x);
      +
      +void test() {
      +  id x = [[NSObject alloc] init];
      +  consume_ns(x); // No leak!
      +}
      +
      +@interface Foo : NSObject
      ++ (void) releaseArg:(id) NS_CONSUMED x;
      ++ (void) releaseSecondArg:(id)x second:(id) NS_CONSUMED y;
      +@end
      +
      +void test_method() {
      +  id x = [[NSObject alloc] init];
      +  [Foo releaseArg:x]; // No leak!
      +}
      +
      +void test_method2() {
      +  id a = [[NSObject alloc] init];
      +  id b = [[NSObject alloc] init];
      +  [Foo releaseSecondArg:a second:b]; // 'a' is leaked, but 'b' is released.
      +}
      +
      + +

      Attribute 'cf_consumed' +(Clang-specific)

      + +

      The 'cf_consumed' attribute is practically identical to ns_consumed. +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 CFRelease upon +completion of the call to the given function or method.

      + +

      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).

      + +

      Example

      + +
      +$ cat test.m
      +#ifndef __has_feature      // Optional.
      +#define __has_feature(x) 0 // Compatibility with non-clang compilers.
      +#endif
      +
      +#ifndef CF_CONSUMED
      +#if __has_feature(attribute_cf_consumed)
      +#define CF_CONSUMED __attribute__((CF_CONSUMED))
      +#else
      +#define CF_CONSUMED
      +#endif
      +#endif
      +
      +void consume_cf(id CF_CONSUMED x);
      +void consume_CFDate(CFDateRef CF_CONSUMED x);
      +
      +void test() {
      +  id x = [[NSObject alloc] init];
      +  consume_cf(x); // No leak!
      +}
      +
      +void test2() {
      +  CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent());
      +  consume_CFDate(date); // No leak, including under GC!
      +  
      +}
      +
      +@interface Foo : NSObject
      ++ (void) releaseArg:(CFDateRef) CF_CONSUMED x;
      +@end
      +
      +void test_method() {
      +  CFDateRef date = CFDateCreate(0, CFAbsoluteTimeGetCurrent());
      +  [Foo releaseArg:date]; // No leak!
      +}
      +
      +

      Attribute 'ns_consumes_self' (Clang-specific)

      @@ -360,7 +467,9 @@ its availability, as it is not available in earlier versions of the analyzer:

      One use of this attribute is declare your own init-like methods that do not follow the - standard Cocoa naming conventions. For example:

      + standard Cocoa naming conventions.

      + +

      Example

       #ifndef __has_feature
      -- 
      2.40.0