Index: include/clang/StaticAnalyzer/Checkers/ObjCRetainCount.h =================================================================== --- include/clang/StaticAnalyzer/Checkers/ObjCRetainCount.h +++ include/clang/StaticAnalyzer/Checkers/ObjCRetainCount.h @@ -145,9 +145,11 @@ /// Indicates that the tracked object is an Objective-C object. ObjC, /// Indicates that the tracked object could be a CF or Objective-C object. - AnyObj + AnyObj, + /// Indicates that the tracked object is a generalized object. + Generalized }; - + private: Kind K; ObjKind O; Index: lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1340,6 +1340,8 @@ if (D->hasAttr()) return RetEffect::MakeOwned(RetEffect::CF); + else if (hasRCAnnotation(D, "rc_ownership_returns_retained")) + return RetEffect::MakeOwned(RetEffect::Generalized); if (D->hasAttr()) return RetEffect::MakeNotOwned(RetEffect::CF); @@ -1363,9 +1365,11 @@ const ParmVarDecl *pd = *pi; if (pd->hasAttr()) Template->addArg(AF, parm_idx, DecRefMsg); - else if (pd->hasAttr()) + else if (pd->hasAttr() || + hasRCAnnotation(pd, "rc_ownership_consumed")) Template->addArg(AF, parm_idx, DecRef); - else if (pd->hasAttr()) { + else if (pd->hasAttr() || + hasRCAnnotation(pd, "rc_ownership_returns_retained")) { QualType PointeeTy = pd->getType()->getPointeeType(); if (!PointeeTy.isNull()) if (coreFoundation::isCFObjectRef(PointeeTy)) @@ -2005,8 +2009,14 @@ os << " returns a Core Foundation object of type " << Sym->getType().getAsString() << " with a "; } - } - else { + } else if (CurrV.getObjKind() == RetEffect::Generalized) { + if (Sym->getType().isNull()) { + os << " returns an object with a "; + } else { + os << " returns an object of type " << Sym->getType().getAsString() + << " with a "; + } + } else { assert (CurrV.getObjKind() == RetEffect::ObjC); QualType T = Sym->getType(); if (T.isNull() || !isa(T)) { Index: test/Analysis/retain-release-inline.m =================================================================== --- test/Analysis/retain-release-inline.m +++ test/Analysis/retain-release-inline.m @@ -299,7 +299,7 @@ bar(s); } -__attribute__((cf_returns_retained)) isl_basic_map *isl_basic_map_cow(__attribute__((cf_consumed)) isl_basic_map *bmap); +__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *isl_basic_map_cow(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap); void free(void *); // As 'isl_basic_map_free' is annotated with 'rc_ownership_trusted_implementation', RetainCountChecker trusts its @@ -307,7 +307,7 @@ // a leak warning is raised by RetainCountChecker as the analyzer is unable to detect a decrement in the reference // count of 'bmap' along the path in 'isl_basic_map_free' assuming the predicate of the second 'if' branch to be // true or assuming both the predicates in the function to be false. -__attribute__((annotate("rc_ownership_trusted_implementation"))) isl_basic_map *isl_basic_map_free(__attribute__((cf_consumed)) isl_basic_map *bmap) { +__attribute__((annotate("rc_ownership_trusted_implementation"))) isl_basic_map *isl_basic_map_free(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap) { if (!bmap) return NULL; @@ -322,7 +322,7 @@ // implementation and doesn't analyze its body. If that annotation is removed, a 'use-after-release' warning might // be raised by RetainCountChecker as the pointer which is passed as an argument to this function and the pointer // which is returned from the function point to the same memory location. -__attribute__((annotate("rc_ownership_trusted_implementation"))) __attribute__((cf_returns_retained)) isl_basic_map *isl_basic_map_copy(isl_basic_map *bmap) { +__attribute__((annotate("rc_ownership_trusted_implementation"))) __attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *isl_basic_map_copy(isl_basic_map *bmap) { if (!bmap) return NULL; @@ -330,7 +330,7 @@ return bmap; } -void test_use_after_release_with_trusted_implementation_annotate_attribute(__attribute__((cf_consumed)) isl_basic_map *bmap) { +void test_use_after_release_with_trusted_implementation_annotate_attribute(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap) { // After this call, 'bmap' has a +1 reference count. bmap = isl_basic_map_cow(bmap); // After the call to 'isl_basic_map_copy', 'bmap' has a +1 reference count. @@ -341,7 +341,7 @@ isl_basic_map_free(temp); } -void test_leak_with_trusted_implementation_annotate_attribute(__attribute__((cf_consumed)) isl_basic_map *bmap) { +void test_leak_with_trusted_implementation_annotate_attribute(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap) { // After this call, 'bmap' has a +1 reference count. bmap = isl_basic_map_cow(bmap); // no-warning // After this call, 'bmap' has a +0 reference count. Index: test/Analysis/retain-release.m =================================================================== --- test/Analysis/retain-release.m +++ test/Analysis/retain-release.m @@ -325,6 +325,9 @@ extern void *CFPlugInInstanceCreate(CFAllocatorRef allocator, CFUUIDRef factoryUUID, CFUUIDRef typeUUID); +typedef struct { + int ref; +} isl_basic_map; //===----------------------------------------------------------------------===// // Test cases. @@ -574,6 +577,14 @@ } } +__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *isl_basic_map_cow(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap); + +// Test custom diagnostics for generalized objects. +void f18(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap) { + // After this call, 'bmap' has a +1 reference count. + bmap = isl_basic_map_cow(bmap); // expected-warning {{Potential leak of an object}} +} + // Test basic tracking of ivars associated with 'self'. For the retain/release // checker we currently do not want to flag leaks associated with stores // of tracked objects to ivars.