Prechádzať zdrojové kódy

[analyzer] Add diagnostic text for generalized refcount annotations.

Add a 'Generalized' object kind to the retain-count checker and suitable
generic diagnostic text for retain-count diagnostics involving those objects.

For now the object kind is introduced in summaries by 'annotate' attributes.
Once we have more experience with these annotations we will propose explicit
attributes.

Patch by Malhar Thakkar!

Differential Revision: https://reviews.llvm.org/D35613

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@308990 91177308-0d34-0410-b5e6-96231b3b80d8
Devin Coughlin 8 rokov pred
rodič
commit
4d083037ef

+ 4 - 2
include/clang/StaticAnalyzer/Checkers/ObjCRetainCount.h

@@ -145,9 +145,11 @@ public:
     /// Indicates that the tracked object is an Objective-C object.
     /// Indicates that the tracked object is an Objective-C object.
     ObjC,
     ObjC,
     /// Indicates that the tracked object could be a CF or Objective-C object.
     /// 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:
 private:
   Kind K;
   Kind K;
   ObjKind O;
   ObjKind O;

+ 13 - 11
lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp

@@ -1340,6 +1340,8 @@ RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy,
 
 
   if (D->hasAttr<CFReturnsRetainedAttr>())
   if (D->hasAttr<CFReturnsRetainedAttr>())
     return RetEffect::MakeOwned(RetEffect::CF);
     return RetEffect::MakeOwned(RetEffect::CF);
+  else if (hasRCAnnotation(D, "rc_ownership_returns_retained"))
+    return RetEffect::MakeOwned(RetEffect::Generalized);
 
 
   if (D->hasAttr<CFReturnsNotRetainedAttr>())
   if (D->hasAttr<CFReturnsNotRetainedAttr>())
     return RetEffect::MakeNotOwned(RetEffect::CF);
     return RetEffect::MakeNotOwned(RetEffect::CF);
@@ -1363,9 +1365,11 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
     const ParmVarDecl *pd = *pi;
     const ParmVarDecl *pd = *pi;
     if (pd->hasAttr<NSConsumedAttr>())
     if (pd->hasAttr<NSConsumedAttr>())
       Template->addArg(AF, parm_idx, DecRefMsg);
       Template->addArg(AF, parm_idx, DecRefMsg);
-    else if (pd->hasAttr<CFConsumedAttr>())
+    else if (pd->hasAttr<CFConsumedAttr>() ||
+             hasRCAnnotation(pd, "rc_ownership_consumed"))
       Template->addArg(AF, parm_idx, DecRef);
       Template->addArg(AF, parm_idx, DecRef);
-    else if (pd->hasAttr<CFReturnsRetainedAttr>()) {
+    else if (pd->hasAttr<CFReturnsRetainedAttr>() ||
+             hasRCAnnotation(pd, "rc_ownership_returns_retained")) {
       QualType PointeeTy = pd->getType()->getPointeeType();
       QualType PointeeTy = pd->getType()->getPointeeType();
       if (!PointeeTy.isNull())
       if (!PointeeTy.isNull())
         if (coreFoundation::isCFObjectRef(PointeeTy))
         if (coreFoundation::isCFObjectRef(PointeeTy))
@@ -1999,17 +2003,15 @@ CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN,
       }
       }
 
 
       if (CurrV.getObjKind() == RetEffect::CF) {
       if (CurrV.getObjKind() == RetEffect::CF) {
-        if (Sym->getType().isNull()) {
-          os << " returns a Core Foundation object with a ";
-        } else {
-          os << " returns a Core Foundation object of type "
-             << Sym->getType().getAsString() << " with a ";
-        }
-      }
-      else {
+        os << " returns a Core Foundation object of type "
+           << Sym->getType().getAsString() << " with a ";
+      } else if (CurrV.getObjKind() == RetEffect::Generalized) {
+        os << " returns an object of type " << Sym->getType().getAsString()
+           << " with a ";
+      } else {
         assert (CurrV.getObjKind() == RetEffect::ObjC);
         assert (CurrV.getObjKind() == RetEffect::ObjC);
         QualType T = Sym->getType();
         QualType T = Sym->getType();
-        if (T.isNull() || !isa<ObjCObjectPointerType>(T)) {
+        if (!isa<ObjCObjectPointerType>(T)) {
           os << " returns an Objective-C object with a ";
           os << " returns an Objective-C object with a ";
         } else {
         } else {
           const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
           const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);

+ 5 - 5
test/Analysis/retain-release-inline.m

@@ -299,7 +299,7 @@ void test_neg() {
   bar(s);
   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 *);
 void free(void *);
 
 
 // As 'isl_basic_map_free' is annotated with 'rc_ownership_trusted_implementation', RetainCountChecker trusts its
 // As 'isl_basic_map_free' is annotated with 'rc_ownership_trusted_implementation', RetainCountChecker trusts its
@@ -307,7 +307,7 @@ void free(void *);
 // a leak warning is raised by RetainCountChecker as the analyzer is unable to detect a decrement in the reference
 // 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
 // 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.
 // 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)
   if (!bmap)
     return NULL;
     return NULL;
 
 
@@ -322,7 +322,7 @@ __attribute__((annotate("rc_ownership_trusted_implementation"))) isl_basic_map *
 // implementation and doesn't analyze its body. If that annotation is removed, a 'use-after-release' warning might
 // 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
 // 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.
 // 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)
   if (!bmap)
     return NULL;
     return NULL;
 
 
@@ -330,7 +330,7 @@ __attribute__((annotate("rc_ownership_trusted_implementation"))) __attribute__((
   return bmap;
   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.
   // After this call, 'bmap' has a +1 reference count.
   bmap = isl_basic_map_cow(bmap);
   bmap = isl_basic_map_cow(bmap);
   // After the call to 'isl_basic_map_copy', 'bmap' has a +1 reference count.
   // After the call to 'isl_basic_map_copy', 'bmap' has a +1 reference count.
@@ -341,7 +341,7 @@ void test_use_after_release_with_trusted_implementation_annotate_attribute(__att
   isl_basic_map_free(temp);
   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.
   // After this call, 'bmap' has a +1 reference count.
   bmap = isl_basic_map_cow(bmap); // no-warning
   bmap = isl_basic_map_cow(bmap); // no-warning
   // After this call, 'bmap' has a +0 reference count.
   // After this call, 'bmap' has a +0 reference count.

+ 11 - 0
test/Analysis/retain-release.m

@@ -325,6 +325,9 @@ typedef const struct __CFUUID * CFUUIDRef;
 
 
 extern
 extern
 void *CFPlugInInstanceCreate(CFAllocatorRef allocator, CFUUIDRef factoryUUID, CFUUIDRef typeUUID);
 void *CFPlugInInstanceCreate(CFAllocatorRef allocator, CFUUIDRef factoryUUID, CFUUIDRef typeUUID);
+typedef struct {
+  int ref;
+} isl_basic_map;
 
 
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
 // Test cases.
 // Test cases.
@@ -574,6 +577,14 @@ void f17(int x, CFTypeRef p) {
   }
   }
 }
 }
 
 
+__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
 // Test basic tracking of ivars associated with 'self'.  For the retain/release
 // checker we currently do not want to flag leaks associated with stores
 // checker we currently do not want to flag leaks associated with stores
 // of tracked objects to ivars.
 // of tracked objects to ivars.