]> granicus.if.org Git - clang/commitdiff
Avoid applying retain/release effects twice in RetainCountChecker when a function...
authorTed Kremenek <kremenek@apple.com>
Fri, 23 Mar 2012 06:26:56 +0000 (06:26 +0000)
committerTed Kremenek <kremenek@apple.com>
Fri, 23 Mar 2012 06:26:56 +0000 (06:26 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@153309 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/CheckerManager.h
include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
lib/StaticAnalyzer/Core/CheckerManager.cpp
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
test/Analysis/retain-release-inline.m
test/Analysis/retain-release.mm

index b8a024b9cc9b763caa108d45be979b516ca539a0..79c94bcd859bcff70bd24f91eb1992b6476e0fea 100644 (file)
@@ -193,14 +193,16 @@ public:
   void runCheckersForPostStmt(ExplodedNodeSet &Dst,
                               const ExplodedNodeSet &Src,
                               const Stmt *S,
-                              ExprEngine &Eng) {
-    runCheckersForStmt(/*isPreVisit=*/false, Dst, Src, S, Eng);
+                              ExprEngine &Eng,
+                              bool wasInlined = false) {
+    runCheckersForStmt(/*isPreVisit=*/false, Dst, Src, S, Eng, wasInlined);
   }
 
   /// \brief Run checkers for visiting Stmts.
   void runCheckersForStmt(bool isPreVisit,
                           ExplodedNodeSet &Dst, const ExplodedNodeSet &Src,
-                          const Stmt *S, ExprEngine &Eng);
+                          const Stmt *S, ExprEngine &Eng,
+                          bool wasInlined = false);
 
   /// \brief Run checkers for pre-visiting obj-c messages.
   void runCheckersForPreObjCMessage(ExplodedNodeSet &Dst,
index 052916177fe318d1073b73d3f4ee4ba39bc18638..b051d33cbd848bc2c88260c853483df5904e8874 100644 (file)
@@ -33,15 +33,21 @@ class CheckerContext {
   NodeBuilder &NB;
 
 public:
+  /// If we are post visiting a call, this flag will be set if the
+  /// call was inlined.  In all other cases it will be false.
+  const bool wasInlined;
+  
   CheckerContext(NodeBuilder &builder,
                  ExprEngine &eng,
                  ExplodedNode *pred,
-                 const ProgramPoint &loc)
+                 const ProgramPoint &loc,
+                 bool wasInlined = false)
     : Eng(eng),
       Pred(pred),
       Changed(false),
       Location(loc),
-      NB(builder) {
+      NB(builder),
+      wasInlined(wasInlined) {
     assert(Pred->getState() &&
            "We should not call the checkers on an empty state.");
   }
index 940228e679539455281043eec7fb952f542e65bd..bf4b76c640512cc1fc4dc54eba2a7080f4a6dad4 100644 (file)
@@ -2599,6 +2599,9 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE,
 
 void RetainCountChecker::checkPostStmt(const CallExpr *CE,
                                        CheckerContext &C) const {
+  if (C.wasInlined)
+    return;
+  
   // Get the callee.
   ProgramStateRef state = C.getState();
   const Expr *Callee = CE->getCallee();
index 6f7a47fb8fead9d0f1723b8695bfdbe18ed64501..e8de329dafa49fbfa7f0ee4d5c7858b3cfa955fb 100644 (file)
@@ -138,13 +138,15 @@ namespace {
     const CheckersTy &Checkers;
     const Stmt *S;
     ExprEngine &Eng;
+    bool wasInlined;
 
     CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
     CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
 
     CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
-                     const Stmt *s, ExprEngine &eng)
-      : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng) { }
+                     const Stmt *s, ExprEngine &eng, bool wasInlined = false)
+      : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng),
+        wasInlined(wasInlined) {}
 
     void runChecker(CheckerManager::CheckStmtFunc checkFn,
                     NodeBuilder &Bldr, ExplodedNode *Pred) {
@@ -153,8 +155,7 @@ namespace {
                                            ProgramPoint::PostStmtKind;
       const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
                                 Pred->getLocationContext(), checkFn.Checker);
-      CheckerContext C(Bldr, Eng, Pred, L);
-
+      CheckerContext C(Bldr, Eng, Pred, L, wasInlined);
       checkFn(S, C);
     }
   };
@@ -165,9 +166,10 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit,
                                         ExplodedNodeSet &Dst,
                                         const ExplodedNodeSet &Src,
                                         const Stmt *S,
-                                        ExprEngine &Eng) {
+                                        ExprEngine &Eng,
+                                        bool wasInlined) {
   CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit),
-                     S, Eng);
+                     S, Eng, wasInlined);
   expandGraphWithCheckers(C, Dst, Src);
 }
 
index 8c9154cf4cefd369a0f43f8ef1a6745cbbaf1375..66842050af9c4dc9ef71b17a7c8db1838e178cac 100644 (file)
@@ -107,7 +107,8 @@ void ExprEngine::processCallExit(ExplodedNode *Pred) {
                                                     &Ctx);
   SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex());
   
-  getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this);
+  getCheckerManager().runCheckersForPostStmt(Dst, N, CE, *this,
+                                             /* wasInlined */ true);
   
   // Enqueue the next element in the block.
   for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I) {
index cbef9995bc6f81bcfbb9a6a952edd83a5a927cf4..610df7f7e94a346887f27f9d030752737d6252f2 100644 (file)
@@ -314,3 +314,34 @@ void test_test_return_retained() {
   [x retain];
   [x release];
 }
+
+//===----------------------------------------------------------------------===//
+// Test not applying "double effects" from inlining and RetainCountChecker summaries.
+// If we inline a call, we should already see its retain/release semantics.
+//===----------------------------------------------------------------------===//
+
+__attribute__((cf_returns_retained)) CFStringRef test_return_inline(CFStringRef x) {
+  CFRetain(x);
+  return x;
+}
+
+void test_test_return_inline(char *bytes) {
+  CFStringRef str = CFStringCreateWithCStringNoCopy(0, bytes, NSNEXTSTEPStringEncoding, 0);
+  // 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);
+  // After this call, 'str2' and 'str' has a +0 reference count.
+  CFRelease(str2);
+}
+
+void test_test_return_inline_2(char *bytes) {
+  CFStringRef str = CFStringCreateWithCStringNoCopy(0, bytes, NSNEXTSTEPStringEncoding, 0); // expected-warning {{leak}}
+  // 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);
+}
+
+
+
index 0b550a8e0f09d95d87d5b5f55764b3a5e518491c..c463f8ada9e2d4a7b75af492932159a3a0e25970 100644 (file)
@@ -341,9 +341,9 @@ int rdar10553686(void)
 }
 int rdar10553686_positive(void)
 {
-  NSObject* bar = static_objc_cast<NSObject*>([[NSObject alloc] init]); // expected-warning {{Potential leak}}
+  NSObject* bar = static_objc_cast<NSObject*>([[NSObject alloc] init]);
   [bar release];
-  [bar retain];
+  [bar retain]; // expected-warning {{used after it is released}}
   return 0;
 }