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 @@ -12,7 +12,7 @@ // // It includes the basic definitions for the test cases below. //===----------------------------------------------------------------------===// - +#define NULL 0 typedef unsigned int __darwin_natural_t; typedef unsigned long uintptr_t; typedef unsigned int uint32_t; @@ -267,6 +267,10 @@ extern CFStringRef CFStringCreateWithCStringNoCopy(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding, CFAllocatorRef contentsDeallocator); +typedef struct { + int ref; +} isl_basic_map; + //===----------------------------------------------------------------------===// // Test cases. //===----------------------------------------------------------------------===// @@ -285,6 +289,7 @@ foo(s); bar(s); } + void test_neg() { NSString *s = [[NSString alloc] init]; // no-warning foo(s); @@ -294,6 +299,48 @@ bar(s); } +__attribute__((cf_returns_retained)) isl_basic_map *isl_basic_map_copy(isl_basic_map *bmap); +__attribute__((cf_returns_retained)) isl_basic_map *isl_basic_map_cow(__attribute__((cf_consumed)) isl_basic_map *bmap); +isl_basic_map *isl_basic_map_decrement_reference_count(__attribute__((cf_consumed)) isl_basic_map *bmap); +void free(void *); + +__attribute__((annotate("rc_ownership_trusted_implementation"))) __attribute__((cf_returns_retained)) isl_basic_map *test_use_after_release_with_trusted_implementation_annotate_attribute(__attribute__((cf_consumed)) isl_basic_map *bmap) { + // After this call, 'temp' has a +1 reference count. + isl_basic_map *temp = isl_basic_map_copy(bmap); + // After this call, 'bmap' has a +1 reference count. + bmap = isl_basic_map_cow(bmap); + // After this call, 'bmap' has a +0 reference count. + isl_basic_map_decrement_reference_count(bmap); + isl_basic_map_decrement_reference_count(bmap); // no-warning {{A 'use-after-release' warning is not raised here as this function has 'rc_ownership_trusted_implementation' annotate attribute}} + return temp; +} + +isl_basic_map *isl_basic_map_free(__attribute__((cf_consumed)) isl_basic_map *bmap) { + if (!bmap) + return NULL; + + if (--bmap->ref > 0) + return NULL; + + free(bmap); + return NULL; +} + +__attribute__((cf_returns_retained)) isl_basic_map *test_leak_with_trusted_implementation_annotate_attribute(__attribute__((cf_consumed)) isl_basic_map *bmap) { + // After this call, 'temp' has a +1 reference count. + isl_basic_map *temp = isl_basic_map_copy(bmap); + // After this call, 'bmap' has a +1 reference count. + bmap = isl_basic_map_cow(bmap); // no-warning {{A 'leak' warning is not raised here as 'foo_bar' (annotated with 'rc_ownership_trusted_implementation') is on the call-stack}} + // After this call, assuming the predicate of the second if branch to be true, 'bmap' has a +1 reference count. + isl_basic_map_free(bmap); + return temp; +} + +__attribute__((annotate("rc_ownership_trusted_implementation"))) __attribute__((cf_returns_retained)) isl_basic_map *foo_bar(__attribute__((cf_consumed)) isl_basic_map *bmap) { + bmap = test_leak_with_trusted_implementation_annotate_attribute(bmap); + return bmap; +} + //===----------------------------------------------------------------------===// // Test returning retained and not-retained values. //===----------------------------------------------------------------------===//