Index: lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1894,6 +1894,23 @@ return SFC->getAnalysisDeclContext()->isBodyAutosynthesized(); } +/// Returns true if any declaration on the call-stack contains +/// 'rc_ownership_trusted_implementation' annotate attribute. +bool isTrustedReferenceCountImplementation(const LocationContext *LCtx) { + while (LCtx) { + if (const StackFrameContext *SFC = dyn_cast(LCtx)) { + const Decl *D = SFC->getDecl(); + for (const auto *Ann : D->specific_attrs()) { + if (Ann->getAnnotation() == "rc_ownership_trusted_implementation") { + return true; + } + } + } + LCtx = LCtx->getParent(); + } + return false; +} + std::shared_ptr CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { @@ -3345,11 +3362,13 @@ } assert(BT); - auto report = std::unique_ptr( - new CFRefReport(*BT, C.getASTContext().getLangOpts(), C.isObjCGCEnabled(), - SummaryLog, N, Sym)); - report->addRange(ErrorRange); - C.emitReport(std::move(report)); + if (!isTrustedReferenceCountImplementation(N->getLocationContext())) { + auto report = std::unique_ptr( + new CFRefReport(*BT, C.getASTContext().getLangOpts(), + C.isObjCGCEnabled(), SummaryLog, N, Sym)); + report->addRange(ErrorRange); + C.emitReport(std::move(report)); + } } //===----------------------------------------------------------------------===// @@ -3579,9 +3598,10 @@ if (N) { const LangOptions &LOpts = C.getASTContext().getLangOpts(); bool GCEnabled = C.isObjCGCEnabled(); - C.emitReport(std::unique_ptr(new CFRefLeakReport( - *getLeakAtReturnBug(LOpts, GCEnabled), LOpts, GCEnabled, - SummaryLog, N, Sym, C, IncludeAllocationLine))); + if (!isTrustedReferenceCountImplementation(N->getLocationContext())) + C.emitReport(std::unique_ptr(new CFRefLeakReport( + *getLeakAtReturnBug(LOpts, GCEnabled), LOpts, GCEnabled, + SummaryLog, N, Sym, C, IncludeAllocationLine))); } } } @@ -3606,9 +3626,10 @@ if (!returnNotOwnedForOwned) returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this)); - C.emitReport(std::unique_ptr(new CFRefReport( - *returnNotOwnedForOwned, C.getASTContext().getLangOpts(), - C.isObjCGCEnabled(), SummaryLog, N, Sym))); + if (!isTrustedReferenceCountImplementation(N->getLocationContext())) + C.emitReport(std::unique_ptr(new CFRefReport( + *returnNotOwnedForOwned, C.getASTContext().getLangOpts(), + C.isObjCGCEnabled(), SummaryLog, N, Sym))); } } } @@ -3811,9 +3832,10 @@ overAutorelease.reset(new OverAutorelease(this)); const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); - Ctx.emitReport(std::unique_ptr( - new CFRefReport(*overAutorelease, LOpts, /* GCEnabled = */ false, - SummaryLog, N, Sym, os.str()))); + if (!isTrustedReferenceCountImplementation(N->getLocationContext())) + Ctx.emitReport(std::unique_ptr( + new CFRefReport(*overAutorelease, LOpts, /* GCEnabled = */ false, + SummaryLog, N, Sym, os.str()))); } return nullptr; @@ -3865,9 +3887,10 @@ : getLeakAtReturnBug(LOpts, GCEnabled); assert(BT && "BugType not initialized."); - Ctx.emitReport(std::unique_ptr( - new CFRefLeakReport(*BT, LOpts, GCEnabled, SummaryLog, N, *I, Ctx, - IncludeAllocationLine))); + if (!isTrustedReferenceCountImplementation(N->getLocationContext())) + Ctx.emitReport(std::unique_ptr( + new CFRefLeakReport(*BT, LOpts, GCEnabled, SummaryLog, N, *I, Ctx, + IncludeAllocationLine))); } } Index: test/Analysis/retain-release-inline.m =================================================================== --- test/Analysis/retain-release-inline.m +++ test/Analysis/retain-release-inline.m @@ -285,6 +285,14 @@ foo(s); bar(s); } + +__attribute__((annotate("rc_ownership_trusted_implementation"))) void test_with_trusted_implementation_annotate_attribute() { + NSString *s = [[NSString alloc] init]; // no-warning {{Ideally, a "leak" warning should've been raised here. But, as this function contains "rc_ownership_trusted_implementation" annotate attribute, the warning is suppressed}} + foo(s); + foo(s); + bar(s); +} + void test_neg() { NSString *s = [[NSString alloc] init]; // no-warning foo(s); @@ -294,6 +302,34 @@ bar(s); } +void test2() { + NSString *s = [[NSString alloc] init]; + foo(s); + foo(s); + bar(s); + bar(s); + bar(s); + [s release]; // expected-warning {{Reference-counted object is used after it is released}} +} + +__attribute__((annotate("rc_ownership_trusted_implementation"))) void test2_with_trusted_implementation_annotate_attribute() { + NSString *s = [[NSString alloc] init]; + foo(s); + foo(s); + bar(s); + bar(s); + bar(s); + [s release]; // no-warning {{Ideally, a "use-after-release" warning should've been raised here. But, as this function contains "rc_ownership_trusted_implementation" annotate attribute, the warning is suppressed}} +} + +void foo_bar() { + NSString *s = [[NSString alloc] init]; // no-warning {{Ideally, a "leak" warning should've been raised. But, as "test3" on the callstack has annotate attribute "rc_ownership_trusted_implementation", the warning is suppressed}} +} + +__attribute__((annotate("rc_ownership_trusted_implementation"))) void test3_with_trusted_implementation_annotate_attribute() { + foo_bar(); +} + //===----------------------------------------------------------------------===// // Test returning retained and not-retained values. //===----------------------------------------------------------------------===// @@ -343,6 +379,14 @@ CFRelease(str); } +__attribute__((annotate("rc_ownership_trusted_implementation"))) void test_test_return_inline_2_with_trusted_implementation_annotate_attribute(char *bytes) { + CFStringRef str = CFStringCreateWithCStringNoCopy(0, bytes, NSNEXTSTEPStringEncoding, 0); // no-warning {{Ideally, a "leak" warning should've been raised here. But, as this function contains "rc_ownership_trusted_implementation" annotate attribute, the warning is suppressed}} + // After this call, 'str' really has +2 reference count. + CFStringRef str2 = test_return_inline(str); + // After this call, 'str' really has a +1 reference count. + CFRelease(str); +} + extern CFStringRef getString(void); CFStringRef testCovariantReturnType(void) __attribute__((cf_returns_retained));