| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 1 | //==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- C++ -*--// | 
| Ted Kremenek | ea6507f | 2008-03-06 00:08:09 +0000 | [diff] [blame] | 2 | // | 
|  | 3 | //                     The LLVM Compiler Infrastructure | 
|  | 4 | // | 
|  | 5 | // This file is distributed under the University of Illinois Open Source | 
|  | 6 | // License. See LICENSE.TXT for details. | 
|  | 7 | // | 
|  | 8 | //===----------------------------------------------------------------------===// | 
|  | 9 | // | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 10 | //  This file defines the methods for RetainCountChecker, which implements | 
|  | 11 | //  a reference count checker for Core Foundation and Cocoa on (Mac OS X). | 
| Ted Kremenek | ea6507f | 2008-03-06 00:08:09 +0000 | [diff] [blame] | 12 | // | 
|  | 13 | //===----------------------------------------------------------------------===// | 
|  | 14 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 15 | #include "ClangSACheckers.h" | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 16 | #include "clang/AST/DeclObjC.h" | 
| Ted Kremenek | 98a24e3 | 2011-03-30 17:41:19 +0000 | [diff] [blame] | 17 | #include "clang/AST/DeclCXX.h" | 
| Ted Kremenek | 2b36f3f | 2010-02-18 00:05:58 +0000 | [diff] [blame] | 18 | #include "clang/Basic/LangOptions.h" | 
|  | 19 | #include "clang/Basic/SourceManager.h" | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 20 | #include "clang/Analysis/DomainSpecific/CocoaConventions.h" | 
| Ted Kremenek | 8e7fbcc | 2011-11-14 21:59:21 +0000 | [diff] [blame] | 21 | #include "clang/AST/ParentMap.h" | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 22 | #include "clang/StaticAnalyzer/Core/Checker.h" | 
|  | 23 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" | 
| Ted Kremenek | f8cbac4 | 2011-02-10 01:03:03 +0000 | [diff] [blame] | 24 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | 
|  | 25 | #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 26 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | 
| Ted Kremenek | 001fd5b | 2011-08-15 22:09:50 +0000 | [diff] [blame] | 27 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" | 
| Ted Kremenek | f8cbac4 | 2011-02-10 01:03:03 +0000 | [diff] [blame] | 28 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" | 
| Jordy Rose | 087611e | 2011-09-02 08:02:59 +0000 | [diff] [blame] | 29 | #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 30 | #include "llvm/ADT/DenseMap.h" | 
|  | 31 | #include "llvm/ADT/FoldingSet.h" | 
| Ted Kremenek | 0747e7e | 2008-10-21 15:53:15 +0000 | [diff] [blame] | 32 | #include "llvm/ADT/ImmutableList.h" | 
| Ted Kremenek | 2b36f3f | 2010-02-18 00:05:58 +0000 | [diff] [blame] | 33 | #include "llvm/ADT/ImmutableMap.h" | 
| Benjamin Kramer | 4903802 | 2012-02-04 13:45:25 +0000 | [diff] [blame] | 34 | #include "llvm/ADT/SmallString.h" | 
| Ted Kremenek | c812b23 | 2008-05-16 18:33:44 +0000 | [diff] [blame] | 35 | #include "llvm/ADT/STLExtras.h" | 
| Ted Kremenek | 2b36f3f | 2010-02-18 00:05:58 +0000 | [diff] [blame] | 36 | #include "llvm/ADT/StringExtras.h" | 
| Chris Lattner | 0e62c1c | 2011-07-23 10:55:15 +0000 | [diff] [blame] | 37 | #include <cstdarg> | 
| Ted Kremenek | ea6507f | 2008-03-06 00:08:09 +0000 | [diff] [blame] | 38 |  | 
|  | 39 | using namespace clang; | 
| Ted Kremenek | 98857c9 | 2010-12-23 07:20:52 +0000 | [diff] [blame] | 40 | using namespace ento; | 
| Ted Kremenek | db1832d | 2010-01-27 06:13:48 +0000 | [diff] [blame] | 41 | using llvm::StrInStrNoCase; | 
| Ted Kremenek | 2855a93 | 2008-11-05 16:54:44 +0000 | [diff] [blame] | 42 |  | 
| Ted Kremenek | a2448b8 | 2010-05-21 21:57:00 +0000 | [diff] [blame] | 43 | namespace { | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 44 | /// Wrapper around different kinds of node builder, so that helper functions | 
|  | 45 | /// can have a common interface. | 
| Francois Pichet | 9b76fa9 | 2011-01-11 10:41:37 +0000 | [diff] [blame] | 46 | class GenericNodeBuilderRefCount { | 
| Anna Zaks | c4aa22c | 2011-10-05 23:44:11 +0000 | [diff] [blame] | 47 | CheckerContext *C; | 
| Ted Kremenek | e8f7316 | 2011-08-12 23:04:46 +0000 | [diff] [blame] | 48 | const ProgramPointTag *tag; | 
| Ted Kremenek | 884a899 | 2009-05-08 23:09:42 +0000 | [diff] [blame] | 49 | public: | 
| Anna Zaks | c4aa22c | 2011-10-05 23:44:11 +0000 | [diff] [blame] | 50 | GenericNodeBuilderRefCount(CheckerContext &c, | 
| Anna Zaks | 3eae334 | 2011-10-25 19:56:48 +0000 | [diff] [blame] | 51 | const ProgramPointTag *t = 0) | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 52 | : C(&c), tag(t){} | 
| Zhongxing Xu | 107f759 | 2009-08-06 12:48:26 +0000 | [diff] [blame] | 53 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 54 | ExplodedNode *MakeNode(ProgramStateRef state, ExplodedNode *Pred, | 
| Anna Zaks | fc0189a | 2011-10-18 23:05:58 +0000 | [diff] [blame] | 55 | bool MarkAsSink = false) { | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 56 | return C->addTransition(state, Pred, tag, MarkAsSink); | 
| Ted Kremenek | 884a899 | 2009-05-08 23:09:42 +0000 | [diff] [blame] | 57 | } | 
|  | 58 | }; | 
|  | 59 | } // end anonymous namespace | 
|  | 60 |  | 
| Ted Kremenek | c8bef6a | 2008-04-09 23:49:11 +0000 | [diff] [blame] | 61 | //===----------------------------------------------------------------------===// | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 62 | // Primitives used for constructing summaries for function/method calls. | 
| Ted Kremenek | c8bef6a | 2008-04-09 23:49:11 +0000 | [diff] [blame] | 63 | //===----------------------------------------------------------------------===// | 
|  | 64 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 65 | /// ArgEffect is used to summarize a function/method call's effect on a | 
|  | 66 | /// particular argument. | 
| Jordy Rose | e8743a7 | 2011-08-24 19:10:50 +0000 | [diff] [blame] | 67 | enum ArgEffect { DoNothing, Autorelease, Dealloc, DecRef, DecRefMsg, | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 68 | DecRefBridgedTransfered, | 
| Jordy Rose | e8743a7 | 2011-08-24 19:10:50 +0000 | [diff] [blame] | 69 | IncRefMsg, IncRef, MakeCollectable, MayEscape, | 
| Ted Kremenek | ea072e3 | 2009-03-17 19:42:23 +0000 | [diff] [blame] | 70 | NewAutoreleasePool, SelfOwn, StopTracking }; | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 71 |  | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 72 | namespace llvm { | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 73 | template <> struct FoldingSetTrait<ArgEffect> { | 
|  | 74 | static inline void Profile(const ArgEffect X, FoldingSetNodeID& ID) { | 
|  | 75 | ID.AddInteger((unsigned) X); | 
|  | 76 | } | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 77 | }; | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 78 | } // end llvm namespace | 
|  | 79 |  | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 80 | /// ArgEffects summarizes the effects of a function/method call on all of | 
|  | 81 | /// its arguments. | 
|  | 82 | typedef llvm::ImmutableMap<unsigned,ArgEffect> ArgEffects; | 
|  | 83 |  | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 84 | namespace { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 85 |  | 
|  | 86 | ///  RetEffect is used to summarize a function/method call's behavior with | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 87 | ///  respect to its return value. | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 88 | class RetEffect { | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 89 | public: | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 90 | enum Kind { NoRet, OwnedSymbol, OwnedAllocatedSymbol, | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 91 | NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol, | 
| Ted Kremenek | 1272f70 | 2009-05-12 20:06:54 +0000 | [diff] [blame] | 92 | OwnedWhenTrackedReceiver }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 93 |  | 
|  | 94 | enum ObjKind { CF, ObjC, AnyObj }; | 
| Ted Kremenek | aeb115f | 2009-01-28 05:56:51 +0000 | [diff] [blame] | 95 |  | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 96 | private: | 
| Ted Kremenek | aeb115f | 2009-01-28 05:56:51 +0000 | [diff] [blame] | 97 | Kind K; | 
|  | 98 | ObjKind O; | 
| Ted Kremenek | aeb115f | 2009-01-28 05:56:51 +0000 | [diff] [blame] | 99 |  | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 100 | RetEffect(Kind k, ObjKind o = AnyObj) : K(k), O(o) {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 101 |  | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 102 | public: | 
| Ted Kremenek | aeb115f | 2009-01-28 05:56:51 +0000 | [diff] [blame] | 103 | Kind getKind() const { return K; } | 
|  | 104 |  | 
|  | 105 | ObjKind getObjKind() const { return O; } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 106 |  | 
| Ted Kremenek | 223a7d5 | 2009-04-29 23:03:22 +0000 | [diff] [blame] | 107 | bool isOwned() const { | 
| Ted Kremenek | 1272f70 | 2009-05-12 20:06:54 +0000 | [diff] [blame] | 108 | return K == OwnedSymbol || K == OwnedAllocatedSymbol || | 
|  | 109 | K == OwnedWhenTrackedReceiver; | 
| Ted Kremenek | 223a7d5 | 2009-04-29 23:03:22 +0000 | [diff] [blame] | 110 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 111 |  | 
| Jordy Rose | 212e459 | 2011-08-23 04:27:15 +0000 | [diff] [blame] | 112 | bool operator==(const RetEffect &Other) const { | 
|  | 113 | return K == Other.K && O == Other.O; | 
|  | 114 | } | 
|  | 115 |  | 
| Ted Kremenek | 1272f70 | 2009-05-12 20:06:54 +0000 | [diff] [blame] | 116 | static RetEffect MakeOwnedWhenTrackedReceiver() { | 
|  | 117 | return RetEffect(OwnedWhenTrackedReceiver, ObjC); | 
|  | 118 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 119 |  | 
| Ted Kremenek | aeb115f | 2009-01-28 05:56:51 +0000 | [diff] [blame] | 120 | static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) { | 
|  | 121 | return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 122 | } | 
| Ted Kremenek | aeb115f | 2009-01-28 05:56:51 +0000 | [diff] [blame] | 123 | static RetEffect MakeNotOwned(ObjKind o) { | 
|  | 124 | return RetEffect(NotOwnedSymbol, o); | 
| Ted Kremenek | e663356 | 2009-04-27 19:14:45 +0000 | [diff] [blame] | 125 | } | 
|  | 126 | static RetEffect MakeGCNotOwned() { | 
|  | 127 | return RetEffect(GCNotOwnedSymbol, ObjC); | 
|  | 128 | } | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 129 | static RetEffect MakeARCNotOwned() { | 
|  | 130 | return RetEffect(ARCNotOwnedSymbol, ObjC); | 
|  | 131 | } | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 132 | static RetEffect MakeNoRet() { | 
|  | 133 | return RetEffect(NoRet); | 
| Ted Kremenek | ab4a8b5 | 2008-06-23 18:02:52 +0000 | [diff] [blame] | 134 | } | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 135 |  | 
|  | 136 | void Profile(llvm::FoldingSetNodeID& ID) const { | 
|  | 137 | ID.AddInteger((unsigned) K); | 
|  | 138 | ID.AddInteger((unsigned) O); | 
|  | 139 | } | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 140 | }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 141 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 142 | //===----------------------------------------------------------------------===// | 
|  | 143 | // Reference-counting logic (typestate + counts). | 
|  | 144 | //===----------------------------------------------------------------------===// | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 145 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 146 | class RefVal { | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 147 | public: | 
|  | 148 | enum Kind { | 
|  | 149 | Owned = 0, // Owning reference. | 
|  | 150 | NotOwned,  // Reference is not owned by still valid (not freed). | 
|  | 151 | Released,  // Object has been released. | 
|  | 152 | ReturnedOwned, // Returned object passes ownership to caller. | 
|  | 153 | ReturnedNotOwned, // Return object does not pass ownership to caller. | 
|  | 154 | ERROR_START, | 
|  | 155 | ErrorDeallocNotOwned, // -dealloc called on non-owned object. | 
|  | 156 | ErrorDeallocGC, // Calling -dealloc with GC enabled. | 
|  | 157 | ErrorUseAfterRelease, // Object used after released. | 
|  | 158 | ErrorReleaseNotOwned, // Release of an object that was not owned. | 
|  | 159 | ERROR_LEAK_START, | 
|  | 160 | ErrorLeak,  // A memory leak due to excessive reference counts. | 
|  | 161 | ErrorLeakReturned, // A memory leak due to the returning method not having | 
|  | 162 | // the correct naming conventions. | 
|  | 163 | ErrorGCLeakReturned, | 
|  | 164 | ErrorOverAutorelease, | 
|  | 165 | ErrorReturnedNotOwned | 
|  | 166 | }; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 167 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 168 | private: | 
|  | 169 | Kind kind; | 
|  | 170 | RetEffect::ObjKind okind; | 
|  | 171 | unsigned Cnt; | 
|  | 172 | unsigned ACnt; | 
|  | 173 | QualType T; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 174 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 175 | RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t) | 
|  | 176 | : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {} | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 177 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 178 | public: | 
|  | 179 | Kind getKind() const { return kind; } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 180 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 181 | RetEffect::ObjKind getObjKind() const { return okind; } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 182 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 183 | unsigned getCount() const { return Cnt; } | 
|  | 184 | unsigned getAutoreleaseCount() const { return ACnt; } | 
|  | 185 | unsigned getCombinedCounts() const { return Cnt + ACnt; } | 
|  | 186 | void clearCounts() { Cnt = 0; ACnt = 0; } | 
|  | 187 | void setCount(unsigned i) { Cnt = i; } | 
|  | 188 | void setAutoreleaseCount(unsigned i) { ACnt = i; } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 189 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 190 | QualType getType() const { return T; } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 191 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 192 | bool isOwned() const { | 
|  | 193 | return getKind() == Owned; | 
|  | 194 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 195 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 196 | bool isNotOwned() const { | 
|  | 197 | return getKind() == NotOwned; | 
|  | 198 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 199 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 200 | bool isReturnedOwned() const { | 
|  | 201 | return getKind() == ReturnedOwned; | 
|  | 202 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 203 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 204 | bool isReturnedNotOwned() const { | 
|  | 205 | return getKind() == ReturnedNotOwned; | 
|  | 206 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 207 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 208 | static RefVal makeOwned(RetEffect::ObjKind o, QualType t, | 
|  | 209 | unsigned Count = 1) { | 
|  | 210 | return RefVal(Owned, o, Count, 0, t); | 
|  | 211 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 212 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 213 | static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t, | 
|  | 214 | unsigned Count = 0) { | 
|  | 215 | return RefVal(NotOwned, o, Count, 0, t); | 
|  | 216 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 217 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 218 | // Comparison, profiling, and pretty-printing. | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 219 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 220 | bool operator==(const RefVal& X) const { | 
|  | 221 | return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt; | 
|  | 222 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 223 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 224 | RefVal operator-(size_t i) const { | 
|  | 225 | return RefVal(getKind(), getObjKind(), getCount() - i, | 
|  | 226 | getAutoreleaseCount(), getType()); | 
|  | 227 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 228 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 229 | RefVal operator+(size_t i) const { | 
|  | 230 | return RefVal(getKind(), getObjKind(), getCount() + i, | 
|  | 231 | getAutoreleaseCount(), getType()); | 
|  | 232 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 233 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 234 | RefVal operator^(Kind k) const { | 
|  | 235 | return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(), | 
|  | 236 | getType()); | 
|  | 237 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 238 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 239 | RefVal autorelease() const { | 
|  | 240 | return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1, | 
|  | 241 | getType()); | 
|  | 242 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 243 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 244 | void Profile(llvm::FoldingSetNodeID& ID) const { | 
|  | 245 | ID.AddInteger((unsigned) kind); | 
|  | 246 | ID.AddInteger(Cnt); | 
|  | 247 | ID.AddInteger(ACnt); | 
|  | 248 | ID.Add(T); | 
|  | 249 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 250 |  | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 251 | void print(raw_ostream &Out) const; | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 252 | }; | 
|  | 253 |  | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 254 | void RefVal::print(raw_ostream &Out) const { | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 255 | if (!T.isNull()) | 
| Jordy Rose | 58a20d3 | 2011-08-28 19:11:56 +0000 | [diff] [blame] | 256 | Out << "Tracked " << T.getAsString() << '/'; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 257 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 258 | switch (getKind()) { | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 259 | default: llvm_unreachable("Invalid RefVal kind"); | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 260 | case Owned: { | 
|  | 261 | Out << "Owned"; | 
|  | 262 | unsigned cnt = getCount(); | 
|  | 263 | if (cnt) Out << " (+ " << cnt << ")"; | 
|  | 264 | break; | 
|  | 265 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 266 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 267 | case NotOwned: { | 
|  | 268 | Out << "NotOwned"; | 
|  | 269 | unsigned cnt = getCount(); | 
|  | 270 | if (cnt) Out << " (+ " << cnt << ")"; | 
|  | 271 | break; | 
|  | 272 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 273 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 274 | case ReturnedOwned: { | 
|  | 275 | Out << "ReturnedOwned"; | 
|  | 276 | unsigned cnt = getCount(); | 
|  | 277 | if (cnt) Out << " (+ " << cnt << ")"; | 
|  | 278 | break; | 
|  | 279 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 280 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 281 | case ReturnedNotOwned: { | 
|  | 282 | Out << "ReturnedNotOwned"; | 
|  | 283 | unsigned cnt = getCount(); | 
|  | 284 | if (cnt) Out << " (+ " << cnt << ")"; | 
|  | 285 | break; | 
|  | 286 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 287 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 288 | case Released: | 
|  | 289 | Out << "Released"; | 
|  | 290 | break; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 291 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 292 | case ErrorDeallocGC: | 
|  | 293 | Out << "-dealloc (GC)"; | 
|  | 294 | break; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 295 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 296 | case ErrorDeallocNotOwned: | 
|  | 297 | Out << "-dealloc (not-owned)"; | 
|  | 298 | break; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 299 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 300 | case ErrorLeak: | 
|  | 301 | Out << "Leaked"; | 
|  | 302 | break; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 303 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 304 | case ErrorLeakReturned: | 
|  | 305 | Out << "Leaked (Bad naming)"; | 
|  | 306 | break; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 307 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 308 | case ErrorGCLeakReturned: | 
|  | 309 | Out << "Leaked (GC-ed at return)"; | 
|  | 310 | break; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 311 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 312 | case ErrorUseAfterRelease: | 
|  | 313 | Out << "Use-After-Release [ERROR]"; | 
|  | 314 | break; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 315 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 316 | case ErrorReleaseNotOwned: | 
|  | 317 | Out << "Release of Not-Owned [ERROR]"; | 
|  | 318 | break; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 319 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 320 | case RefVal::ErrorOverAutorelease: | 
|  | 321 | Out << "Over autoreleased"; | 
|  | 322 | break; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 323 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 324 | case RefVal::ErrorReturnedNotOwned: | 
|  | 325 | Out << "Non-owned object returned instead of owned"; | 
|  | 326 | break; | 
|  | 327 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 328 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 329 | if (ACnt) { | 
|  | 330 | Out << " [ARC +" << ACnt << ']'; | 
|  | 331 | } | 
|  | 332 | } | 
|  | 333 | } //end anonymous namespace | 
|  | 334 |  | 
|  | 335 | //===----------------------------------------------------------------------===// | 
|  | 336 | // RefBindings - State used to track object reference counts. | 
|  | 337 | //===----------------------------------------------------------------------===// | 
|  | 338 |  | 
|  | 339 | typedef llvm::ImmutableMap<SymbolRef, RefVal> RefBindings; | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 340 |  | 
|  | 341 | namespace clang { | 
| Ted Kremenek | 98857c9 | 2010-12-23 07:20:52 +0000 | [diff] [blame] | 342 | namespace ento { | 
| Ted Kremenek | 001fd5b | 2011-08-15 22:09:50 +0000 | [diff] [blame] | 343 | template<> | 
|  | 344 | struct ProgramStateTrait<RefBindings> | 
|  | 345 | : public ProgramStatePartialTrait<RefBindings> { | 
|  | 346 | static void *GDMIndex() { | 
|  | 347 | static int RefBIndex = 0; | 
|  | 348 | return &RefBIndex; | 
|  | 349 | } | 
|  | 350 | }; | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 351 | } | 
| Argyrios Kyrtzidis | ca08fba | 2010-12-22 18:53:20 +0000 | [diff] [blame] | 352 | } | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 353 |  | 
|  | 354 | //===----------------------------------------------------------------------===// | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 355 | // Function/Method behavior summaries. | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 356 | //===----------------------------------------------------------------------===// | 
|  | 357 |  | 
|  | 358 | namespace { | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 359 | class RetainSummary { | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 360 | /// Args - a map of (index, ArgEffect) pairs, where index | 
| Ted Kremenek | cb2e636 | 2008-05-06 15:44:25 +0000 | [diff] [blame] | 361 | ///  specifies the argument (starting from 0).  This can be sparsely | 
|  | 362 | ///  populated; arguments with no entry in Args use 'DefaultArgEffect'. | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 363 | ArgEffects Args; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 364 |  | 
| Ted Kremenek | cb2e636 | 2008-05-06 15:44:25 +0000 | [diff] [blame] | 365 | /// DefaultArgEffect - The default ArgEffect to apply to arguments that | 
|  | 366 | ///  do not have an entry in Args. | 
| Ted Kremenek | e8300e5 | 2012-01-04 00:35:45 +0000 | [diff] [blame] | 367 | ArgEffect DefaultArgEffect; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 368 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 369 | /// Receiver - If this summary applies to an Objective-C message expression, | 
|  | 370 | ///  this is the effect applied to the state of the receiver. | 
| Ted Kremenek | e8300e5 | 2012-01-04 00:35:45 +0000 | [diff] [blame] | 371 | ArgEffect Receiver; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 372 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 373 | /// Ret - The effect on the return value.  Used to indicate if the | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 374 | ///  function/method call returns a new tracked symbol. | 
| Ted Kremenek | e8300e5 | 2012-01-04 00:35:45 +0000 | [diff] [blame] | 375 | RetEffect Ret; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 376 |  | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 377 | public: | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 378 | RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff, | 
| Jordy Rose | 5a3c9ff | 2011-08-20 20:55:40 +0000 | [diff] [blame] | 379 | ArgEffect ReceiverEff) | 
|  | 380 | : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R) {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 381 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 382 | /// getArg - Return the argument effect on the argument specified by | 
|  | 383 | ///  idx (starting from 0). | 
| Ted Kremenek | bf9d804 | 2008-03-11 17:48:22 +0000 | [diff] [blame] | 384 | ArgEffect getArg(unsigned idx) const { | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 385 | if (const ArgEffect *AE = Args.lookup(idx)) | 
|  | 386 | return *AE; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 387 |  | 
| Ted Kremenek | cb2e636 | 2008-05-06 15:44:25 +0000 | [diff] [blame] | 388 | return DefaultArgEffect; | 
| Ted Kremenek | bf9d804 | 2008-03-11 17:48:22 +0000 | [diff] [blame] | 389 | } | 
| Ted Kremenek | afe348e | 2011-01-27 18:43:03 +0000 | [diff] [blame] | 390 |  | 
|  | 391 | void addArg(ArgEffects::Factory &af, unsigned idx, ArgEffect e) { | 
|  | 392 | Args = af.add(Args, idx, e); | 
|  | 393 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 394 |  | 
| Ted Kremenek | 1d9a267 | 2009-05-04 05:31:22 +0000 | [diff] [blame] | 395 | /// setDefaultArgEffect - Set the default argument effect. | 
|  | 396 | void setDefaultArgEffect(ArgEffect E) { | 
|  | 397 | DefaultArgEffect = E; | 
|  | 398 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 399 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 400 | /// getRetEffect - Returns the effect on the return value of the call. | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 401 | RetEffect getRetEffect() const { return Ret; } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 402 |  | 
| Ted Kremenek | 1d9a267 | 2009-05-04 05:31:22 +0000 | [diff] [blame] | 403 | /// setRetEffect - Set the effect of the return value of the call. | 
|  | 404 | void setRetEffect(RetEffect E) { Ret = E; } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 405 |  | 
| Ted Kremenek | 0e89838 | 2011-01-27 06:54:14 +0000 | [diff] [blame] | 406 |  | 
|  | 407 | /// Sets the effect on the receiver of the message. | 
|  | 408 | void setReceiverEffect(ArgEffect e) { Receiver = e; } | 
|  | 409 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 410 | /// getReceiverEffect - Returns the effect on the receiver of the call. | 
|  | 411 | ///  This is only meaningful if the summary applies to an ObjCMessageExpr*. | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 412 | ArgEffect getReceiverEffect() const { return Receiver; } | 
| Jordy Rose | 212e459 | 2011-08-23 04:27:15 +0000 | [diff] [blame] | 413 |  | 
|  | 414 | /// Test if two retain summaries are identical. Note that merely equivalent | 
|  | 415 | /// summaries are not necessarily identical (for example, if an explicit | 
|  | 416 | /// argument effect matches the default effect). | 
|  | 417 | bool operator==(const RetainSummary &Other) const { | 
|  | 418 | return Args == Other.Args && DefaultArgEffect == Other.DefaultArgEffect && | 
|  | 419 | Receiver == Other.Receiver && Ret == Other.Ret; | 
|  | 420 | } | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 421 |  | 
|  | 422 | /// Profile this summary for inclusion in a FoldingSet. | 
|  | 423 | void Profile(llvm::FoldingSetNodeID& ID) const { | 
|  | 424 | ID.Add(Args); | 
|  | 425 | ID.Add(DefaultArgEffect); | 
|  | 426 | ID.Add(Receiver); | 
|  | 427 | ID.Add(Ret); | 
|  | 428 | } | 
|  | 429 |  | 
|  | 430 | /// A retain summary is simple if it has no ArgEffects other than the default. | 
|  | 431 | bool isSimple() const { | 
|  | 432 | return Args.isEmpty(); | 
|  | 433 | } | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 434 | }; | 
| Ted Kremenek | 0cfc161 | 2008-06-23 23:30:29 +0000 | [diff] [blame] | 435 | } // end anonymous namespace | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 436 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 437 | //===----------------------------------------------------------------------===// | 
|  | 438 | // Data structures for constructing summaries. | 
|  | 439 | //===----------------------------------------------------------------------===// | 
| Ted Kremenek | b1d1329 | 2008-06-24 03:49:48 +0000 | [diff] [blame] | 440 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 441 | namespace { | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 442 | class ObjCSummaryKey { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 443 | IdentifierInfo* II; | 
|  | 444 | Selector S; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 445 | public: | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 446 | ObjCSummaryKey(IdentifierInfo* ii, Selector s) | 
|  | 447 | : II(ii), S(s) {} | 
|  | 448 |  | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 449 | ObjCSummaryKey(const ObjCInterfaceDecl *d, Selector s) | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 450 | : II(d ? d->getIdentifier() : 0), S(s) {} | 
| Ted Kremenek | 5801f65 | 2009-05-13 18:16:01 +0000 | [diff] [blame] | 451 |  | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 452 | ObjCSummaryKey(const ObjCInterfaceDecl *d, IdentifierInfo *ii, Selector s) | 
| Ted Kremenek | 5801f65 | 2009-05-13 18:16:01 +0000 | [diff] [blame] | 453 | : II(d ? d->getIdentifier() : ii), S(s) {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 454 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 455 | ObjCSummaryKey(Selector s) | 
|  | 456 | : II(0), S(s) {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 457 |  | 
| Ted Kremenek | e8300e5 | 2012-01-04 00:35:45 +0000 | [diff] [blame] | 458 | IdentifierInfo *getIdentifier() const { return II; } | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 459 | Selector getSelector() const { return S; } | 
|  | 460 | }; | 
| Ted Kremenek | 0cfc161 | 2008-06-23 23:30:29 +0000 | [diff] [blame] | 461 | } | 
|  | 462 |  | 
|  | 463 | namespace llvm { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 464 | template <> struct DenseMapInfo<ObjCSummaryKey> { | 
|  | 465 | static inline ObjCSummaryKey getEmptyKey() { | 
|  | 466 | return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getEmptyKey(), | 
|  | 467 | DenseMapInfo<Selector>::getEmptyKey()); | 
|  | 468 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 469 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 470 | static inline ObjCSummaryKey getTombstoneKey() { | 
|  | 471 | return ObjCSummaryKey(DenseMapInfo<IdentifierInfo*>::getTombstoneKey(), | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 472 | DenseMapInfo<Selector>::getTombstoneKey()); | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 473 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 474 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 475 | static unsigned getHashValue(const ObjCSummaryKey &V) { | 
| Benjamin Kramer | 69b5a60 | 2012-05-27 13:28:44 +0000 | [diff] [blame] | 476 | typedef std::pair<IdentifierInfo*, Selector> PairTy; | 
|  | 477 | return DenseMapInfo<PairTy>::getHashValue(PairTy(V.getIdentifier(), | 
|  | 478 | V.getSelector())); | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 479 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 480 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 481 | static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) { | 
| Benjamin Kramer | 69b5a60 | 2012-05-27 13:28:44 +0000 | [diff] [blame] | 482 | return LHS.getIdentifier() == RHS.getIdentifier() && | 
|  | 483 | LHS.getSelector() == RHS.getSelector(); | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 484 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 485 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 486 | }; | 
| Chris Lattner | 2f3da9b | 2009-12-15 07:26:51 +0000 | [diff] [blame] | 487 | template <> | 
|  | 488 | struct isPodLike<ObjCSummaryKey> { static const bool value = true; }; | 
| Ted Kremenek | 0cfc161 | 2008-06-23 23:30:29 +0000 | [diff] [blame] | 489 | } // end llvm namespace | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 490 |  | 
| Ted Kremenek | 0cfc161 | 2008-06-23 23:30:29 +0000 | [diff] [blame] | 491 | namespace { | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 492 | class ObjCSummaryCache { | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 493 | typedef llvm::DenseMap<ObjCSummaryKey, const RetainSummary *> MapTy; | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 494 | MapTy M; | 
|  | 495 | public: | 
|  | 496 | ObjCSummaryCache() {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 497 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 498 | const RetainSummary * find(const ObjCInterfaceDecl *D, IdentifierInfo *ClsName, | 
| Ted Kremenek | 223a7d5 | 2009-04-29 23:03:22 +0000 | [diff] [blame] | 499 | Selector S) { | 
| Ted Kremenek | 0b50fb1 | 2009-04-29 05:04:30 +0000 | [diff] [blame] | 500 | // Lookup the method using the decl for the class @interface.  If we | 
|  | 501 | // have no decl, lookup using the class name. | 
|  | 502 | return D ? find(D, S) : find(ClsName, S); | 
|  | 503 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 504 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 505 | const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 506 | // Do a lookup with the (D,S) pair.  If we find a match return | 
|  | 507 | // the iterator. | 
|  | 508 | ObjCSummaryKey K(D, S); | 
|  | 509 | MapTy::iterator I = M.find(K); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 510 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 511 | if (I != M.end() || !D) | 
| Ted Kremenek | 8be5138 | 2009-07-21 23:27:57 +0000 | [diff] [blame] | 512 | return I->second; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 513 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 514 | // Walk the super chain.  If we find a hit with a parent, we'll end | 
|  | 515 | // up returning that summary.  We actually allow that key (null,S), as | 
|  | 516 | // we cache summaries for the null ObjCInterfaceDecl* to allow us to | 
|  | 517 | // generate initial summaries without having to worry about NSObject | 
|  | 518 | // being declared. | 
|  | 519 | // FIXME: We may change this at some point. | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 520 | for (ObjCInterfaceDecl *C=D->getSuperClass() ;; C=C->getSuperClass()) { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 521 | if ((I = M.find(ObjCSummaryKey(C, S))) != M.end()) | 
|  | 522 | break; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 523 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 524 | if (!C) | 
| Ted Kremenek | 8be5138 | 2009-07-21 23:27:57 +0000 | [diff] [blame] | 525 | return NULL; | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 526 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 527 |  | 
|  | 528 | // Cache the summary with original key to make the next lookup faster | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 529 | // and return the iterator. | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 530 | const RetainSummary *Summ = I->second; | 
| Ted Kremenek | 8be5138 | 2009-07-21 23:27:57 +0000 | [diff] [blame] | 531 | M[K] = Summ; | 
|  | 532 | return Summ; | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 533 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 534 |  | 
| Ted Kremenek | e8300e5 | 2012-01-04 00:35:45 +0000 | [diff] [blame] | 535 | const RetainSummary *find(IdentifierInfo* II, Selector S) { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 536 | // FIXME: Class method lookup.  Right now we dont' have a good way | 
|  | 537 | // of going between IdentifierInfo* and the class hierarchy. | 
| Ted Kremenek | 8be5138 | 2009-07-21 23:27:57 +0000 | [diff] [blame] | 538 | MapTy::iterator I = M.find(ObjCSummaryKey(II, S)); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 539 |  | 
| Ted Kremenek | 8be5138 | 2009-07-21 23:27:57 +0000 | [diff] [blame] | 540 | if (I == M.end()) | 
|  | 541 | I = M.find(ObjCSummaryKey(S)); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 542 |  | 
| Ted Kremenek | 8be5138 | 2009-07-21 23:27:57 +0000 | [diff] [blame] | 543 | return I == M.end() ? NULL : I->second; | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 544 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 545 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 546 | const RetainSummary *& operator[](ObjCSummaryKey K) { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 547 | return M[K]; | 
|  | 548 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 549 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 550 | const RetainSummary *& operator[](Selector S) { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 551 | return M[ ObjCSummaryKey(S) ]; | 
|  | 552 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 553 | }; | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 554 | } // end anonymous namespace | 
|  | 555 |  | 
|  | 556 | //===----------------------------------------------------------------------===// | 
|  | 557 | // Data structures for managing collections of summaries. | 
|  | 558 | //===----------------------------------------------------------------------===// | 
|  | 559 |  | 
|  | 560 | namespace { | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 561 | class RetainSummaryManager { | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 562 |  | 
|  | 563 | //==-----------------------------------------------------------------==// | 
|  | 564 | //  Typedefs. | 
|  | 565 | //==-----------------------------------------------------------------==// | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 566 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 567 | typedef llvm::DenseMap<const FunctionDecl*, const RetainSummary *> | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 568 | FuncSummariesTy; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 569 |  | 
| Ted Kremenek | 0cfc161 | 2008-06-23 23:30:29 +0000 | [diff] [blame] | 570 | typedef ObjCSummaryCache ObjCMethodSummariesTy; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 571 |  | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 572 | typedef llvm::FoldingSetNodeWrapper<RetainSummary> CachedSummaryNode; | 
|  | 573 |  | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 574 | //==-----------------------------------------------------------------==// | 
|  | 575 | //  Data. | 
|  | 576 | //==-----------------------------------------------------------------==// | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 577 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 578 | /// Ctx - The ASTContext object for the analyzed ASTs. | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 579 | ASTContext &Ctx; | 
| Ted Kremenek | ab54e51 | 2008-07-01 17:21:27 +0000 | [diff] [blame] | 580 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 581 | /// GCEnabled - Records whether or not the analyzed code runs in GC mode. | 
| Ted Kremenek | 4b7ca77 | 2008-04-29 05:33:51 +0000 | [diff] [blame] | 582 | const bool GCEnabled; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 583 |  | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 584 | /// Records whether or not the analyzed code runs in ARC mode. | 
|  | 585 | const bool ARCEnabled; | 
|  | 586 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 587 | /// FuncSummaries - A map from FunctionDecls to summaries. | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 588 | FuncSummariesTy FuncSummaries; | 
|  | 589 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 590 | /// ObjCClassMethodSummaries - A map from selectors (for instance methods) | 
|  | 591 | ///  to summaries. | 
| Ted Kremenek | ea736c5 | 2008-06-23 22:21:20 +0000 | [diff] [blame] | 592 | ObjCMethodSummariesTy ObjCClassMethodSummaries; | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 593 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 594 | /// ObjCMethodSummaries - A map from selectors to summaries. | 
| Ted Kremenek | ea736c5 | 2008-06-23 22:21:20 +0000 | [diff] [blame] | 595 | ObjCMethodSummariesTy ObjCMethodSummaries; | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 596 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 597 | /// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects, | 
|  | 598 | ///  and all other data used by the checker. | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 599 | llvm::BumpPtrAllocator BPAlloc; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 600 |  | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 601 | /// AF - A factory for ArgEffects objects. | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 602 | ArgEffects::Factory AF; | 
|  | 603 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 604 | /// ScratchArgs - A holding buffer for construct ArgEffects. | 
| Ted Kremenek | e8300e5 | 2012-01-04 00:35:45 +0000 | [diff] [blame] | 605 | ArgEffects ScratchArgs; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 606 |  | 
| Ted Kremenek | 9157fbb | 2009-05-07 23:40:42 +0000 | [diff] [blame] | 607 | /// ObjCAllocRetE - Default return effect for methods returning Objective-C | 
|  | 608 | ///  objects. | 
|  | 609 | RetEffect ObjCAllocRetE; | 
| Ted Kremenek | a03705c | 2009-06-05 23:18:01 +0000 | [diff] [blame] | 610 |  | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 611 | /// ObjCInitRetE - Default return effect for init methods returning | 
| Ted Kremenek | 815fbb6 | 2009-08-20 05:13:36 +0000 | [diff] [blame] | 612 | ///   Objective-C objects. | 
| Ted Kremenek | a03705c | 2009-06-05 23:18:01 +0000 | [diff] [blame] | 613 | RetEffect ObjCInitRetE; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 614 |  | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 615 | /// SimpleSummaries - Used for uniquing summaries that don't have special | 
|  | 616 | /// effects. | 
|  | 617 | llvm::FoldingSet<CachedSummaryNode> SimpleSummaries; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 618 |  | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 619 | //==-----------------------------------------------------------------==// | 
|  | 620 | //  Methods. | 
|  | 621 | //==-----------------------------------------------------------------==// | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 622 |  | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 623 | /// getArgEffects - Returns a persistent ArgEffects object based on the | 
|  | 624 | ///  data in ScratchArgs. | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 625 | ArgEffects getArgEffects(); | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 626 |  | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 627 | enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable }; | 
|  | 628 |  | 
| Ted Kremenek | cc3d188 | 2008-10-23 01:56:15 +0000 | [diff] [blame] | 629 | public: | 
| Ted Kremenek | 1272f70 | 2009-05-12 20:06:54 +0000 | [diff] [blame] | 630 | RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 631 |  | 
| Ted Kremenek | e8300e5 | 2012-01-04 00:35:45 +0000 | [diff] [blame] | 632 | const RetainSummary *getUnarySummary(const FunctionType* FT, | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 633 | UnaryFuncKind func); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 634 |  | 
| Ted Kremenek | e8300e5 | 2012-01-04 00:35:45 +0000 | [diff] [blame] | 635 | const RetainSummary *getCFSummaryCreateRule(const FunctionDecl *FD); | 
|  | 636 | const RetainSummary *getCFSummaryGetRule(const FunctionDecl *FD); | 
|  | 637 | const RetainSummary *getCFCreateGetRuleSummary(const FunctionDecl *FD); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 638 |  | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 639 | const RetainSummary *getPersistentSummary(const RetainSummary &OldSumm); | 
| Ted Kremenek | 3700b76 | 2008-10-29 04:07:07 +0000 | [diff] [blame] | 640 |  | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 641 | const RetainSummary *getPersistentSummary(RetEffect RetEff, | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 642 | ArgEffect ReceiverEff = DoNothing, | 
|  | 643 | ArgEffect DefaultEff = MayEscape) { | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 644 | RetainSummary Summ(getArgEffects(), RetEff, DefaultEff, ReceiverEff); | 
|  | 645 | return getPersistentSummary(Summ); | 
|  | 646 | } | 
|  | 647 |  | 
| Ted Kremenek | ececf9f | 2012-05-08 00:12:09 +0000 | [diff] [blame] | 648 | const RetainSummary *getDoNothingSummary() { | 
|  | 649 | return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); | 
|  | 650 | } | 
|  | 651 |  | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 652 | const RetainSummary *getDefaultSummary() { | 
|  | 653 | return getPersistentSummary(RetEffect::MakeNoRet(), | 
|  | 654 | DoNothing, MayEscape); | 
| Ted Kremenek | 0806f91 | 2008-05-06 00:30:21 +0000 | [diff] [blame] | 655 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 656 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 657 | const RetainSummary *getPersistentStopSummary() { | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 658 | return getPersistentSummary(RetEffect::MakeNoRet(), | 
|  | 659 | StopTracking, StopTracking); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 660 | } | 
| Ted Kremenek | 015c356 | 2008-05-06 04:20:12 +0000 | [diff] [blame] | 661 |  | 
| Ted Kremenek | ea736c5 | 2008-06-23 22:21:20 +0000 | [diff] [blame] | 662 | void InitializeClassMethodSummaries(); | 
|  | 663 | void InitializeMethodSummaries(); | 
| Ted Kremenek | cc3d188 | 2008-10-23 01:56:15 +0000 | [diff] [blame] | 664 | private: | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 665 | void addNSObjectClsMethSummary(Selector S, const RetainSummary *Summ) { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 666 | ObjCClassMethodSummaries[S] = Summ; | 
|  | 667 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 668 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 669 | void addNSObjectMethSummary(Selector S, const RetainSummary *Summ) { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 670 | ObjCMethodSummaries[S] = Summ; | 
|  | 671 | } | 
| Ted Kremenek | 00dfe30 | 2009-03-04 23:30:42 +0000 | [diff] [blame] | 672 |  | 
| Ted Kremenek | e8a5ba8 | 2012-02-18 21:37:48 +0000 | [diff] [blame] | 673 | void addClassMethSummary(const char* Cls, const char* name, | 
|  | 674 | const RetainSummary *Summ, bool isNullary = true) { | 
| Ted Kremenek | 00dfe30 | 2009-03-04 23:30:42 +0000 | [diff] [blame] | 675 | IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); | 
| Ted Kremenek | e8a5ba8 | 2012-02-18 21:37:48 +0000 | [diff] [blame] | 676 | Selector S = isNullary ? GetNullarySelector(name, Ctx) | 
|  | 677 | : GetUnarySelector(name, Ctx); | 
| Ted Kremenek | 00dfe30 | 2009-03-04 23:30:42 +0000 | [diff] [blame] | 678 | ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)]  = Summ; | 
|  | 679 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 680 |  | 
| Ted Kremenek | dce7846 | 2009-02-25 02:54:57 +0000 | [diff] [blame] | 681 | void addInstMethSummary(const char* Cls, const char* nullaryName, | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 682 | const RetainSummary *Summ) { | 
| Ted Kremenek | dce7846 | 2009-02-25 02:54:57 +0000 | [diff] [blame] | 683 | IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); | 
|  | 684 | Selector S = GetNullarySelector(nullaryName, Ctx); | 
|  | 685 | ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)]  = Summ; | 
|  | 686 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 687 |  | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 688 | Selector generateSelector(va_list argp) { | 
| Chris Lattner | 0e62c1c | 2011-07-23 10:55:15 +0000 | [diff] [blame] | 689 | SmallVector<IdentifierInfo*, 10> II; | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 690 |  | 
| Ted Kremenek | 050b91c | 2008-08-12 18:30:56 +0000 | [diff] [blame] | 691 | while (const char* s = va_arg(argp, const char*)) | 
|  | 692 | II.push_back(&Ctx.Idents.get(s)); | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 693 |  | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 694 | return Ctx.Selectors.getSelector(II.size(), &II[0]); | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 695 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 696 |  | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 697 | void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries, | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 698 | const RetainSummary * Summ, va_list argp) { | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 699 | Selector S = generateSelector(argp); | 
|  | 700 | Summaries[ObjCSummaryKey(ClsII, S)] = Summ; | 
| Ted Kremenek | 3b2294c | 2008-07-18 17:24:20 +0000 | [diff] [blame] | 701 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 702 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 703 | void addInstMethSummary(const char* Cls, const RetainSummary * Summ, ...) { | 
| Ted Kremenek | 3f13f59 | 2008-08-12 18:48:50 +0000 | [diff] [blame] | 704 | va_list argp; | 
|  | 705 | va_start(argp, Summ); | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 706 | addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 707 | va_end(argp); | 
| Ted Kremenek | 3f13f59 | 2008-08-12 18:48:50 +0000 | [diff] [blame] | 708 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 709 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 710 | void addClsMethSummary(const char* Cls, const RetainSummary * Summ, ...) { | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 711 | va_list argp; | 
|  | 712 | va_start(argp, Summ); | 
|  | 713 | addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp); | 
|  | 714 | va_end(argp); | 
|  | 715 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 716 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 717 | void addClsMethSummary(IdentifierInfo *II, const RetainSummary * Summ, ...) { | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 718 | va_list argp; | 
|  | 719 | va_start(argp, Summ); | 
|  | 720 | addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp); | 
|  | 721 | va_end(argp); | 
|  | 722 | } | 
|  | 723 |  | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 724 | public: | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 725 |  | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 726 | RetainSummaryManager(ASTContext &ctx, bool gcenabled, bool usesARC) | 
| Ted Kremenek | ab54e51 | 2008-07-01 17:21:27 +0000 | [diff] [blame] | 727 | : Ctx(ctx), | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 728 | GCEnabled(gcenabled), | 
|  | 729 | ARCEnabled(usesARC), | 
|  | 730 | AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), | 
|  | 731 | ObjCAllocRetE(gcenabled | 
|  | 732 | ? RetEffect::MakeGCNotOwned() | 
|  | 733 | : (usesARC ? RetEffect::MakeARCNotOwned() | 
|  | 734 | : RetEffect::MakeOwned(RetEffect::ObjC, true))), | 
|  | 735 | ObjCInitRetE(gcenabled | 
|  | 736 | ? RetEffect::MakeGCNotOwned() | 
|  | 737 | : (usesARC ? RetEffect::MakeARCNotOwned() | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 738 | : RetEffect::MakeOwnedWhenTrackedReceiver())) { | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 739 | InitializeClassMethodSummaries(); | 
|  | 740 | InitializeMethodSummaries(); | 
|  | 741 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 742 |  | 
| Anna Zaks | f4c5ea5 | 2012-05-04 22:18:39 +0000 | [diff] [blame] | 743 | const RetainSummary *getSummary(const FunctionDecl *FD, | 
|  | 744 | const CallOrObjCMessage *CME = 0); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 745 |  | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 746 | const RetainSummary *getMethodSummary(Selector S, IdentifierInfo *ClsName, | 
|  | 747 | const ObjCInterfaceDecl *ID, | 
|  | 748 | const ObjCMethodDecl *MD, | 
|  | 749 | QualType RetTy, | 
|  | 750 | ObjCMethodSummariesTy &CachedSummaries); | 
|  | 751 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 752 | const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 753 | ProgramStateRef state, | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 754 | const LocationContext *LC); | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 755 |  | 
| Ted Kremenek | e8300e5 | 2012-01-04 00:35:45 +0000 | [diff] [blame] | 756 | const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 757 | const ObjCInterfaceDecl *ID) { | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 758 | return getMethodSummary(msg.getSelector(), 0, ID, msg.getMethodDecl(), | 
|  | 759 | msg.getType(Ctx), ObjCMethodSummaries); | 
| Ted Kremenek | 0b50fb1 | 2009-04-29 05:04:30 +0000 | [diff] [blame] | 760 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 761 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 762 | const RetainSummary *getClassMethodSummary(const ObjCMessage &msg) { | 
| Argyrios Kyrtzidis | 37ab726 | 2011-01-25 00:03:53 +0000 | [diff] [blame] | 763 | const ObjCInterfaceDecl *Class = 0; | 
|  | 764 | if (!msg.isInstanceMessage()) | 
|  | 765 | Class = msg.getReceiverInterface(); | 
| Douglas Gregor | 9a12919 | 2010-04-21 00:45:42 +0000 | [diff] [blame] | 766 |  | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 767 | return getMethodSummary(msg.getSelector(), Class->getIdentifier(), | 
|  | 768 | Class, msg.getMethodDecl(), msg.getType(Ctx), | 
|  | 769 | ObjCClassMethodSummaries); | 
| Ted Kremenek | 7686ffa | 2009-04-29 00:42:39 +0000 | [diff] [blame] | 770 | } | 
| Ted Kremenek | 99fe169 | 2009-04-29 17:17:48 +0000 | [diff] [blame] | 771 |  | 
|  | 772 | /// getMethodSummary - This version of getMethodSummary is used to query | 
|  | 773 | ///  the summary for the current method being analyzed. | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 774 | const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) { | 
| Ted Kremenek | 223a7d5 | 2009-04-29 23:03:22 +0000 | [diff] [blame] | 775 | // FIXME: Eventually this should be unneeded. | 
| Ted Kremenek | 223a7d5 | 2009-04-29 23:03:22 +0000 | [diff] [blame] | 776 | const ObjCInterfaceDecl *ID = MD->getClassInterface(); | 
| Ted Kremenek | b2a143f | 2009-04-30 05:41:14 +0000 | [diff] [blame] | 777 | Selector S = MD->getSelector(); | 
| Ted Kremenek | 99fe169 | 2009-04-29 17:17:48 +0000 | [diff] [blame] | 778 | IdentifierInfo *ClsName = ID->getIdentifier(); | 
|  | 779 | QualType ResultTy = MD->getResultType(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 780 |  | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 781 | ObjCMethodSummariesTy *CachedSummaries; | 
| Ted Kremenek | 99fe169 | 2009-04-29 17:17:48 +0000 | [diff] [blame] | 782 | if (MD->isInstanceMethod()) | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 783 | CachedSummaries = &ObjCMethodSummaries; | 
| Ted Kremenek | 99fe169 | 2009-04-29 17:17:48 +0000 | [diff] [blame] | 784 | else | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 785 | CachedSummaries = &ObjCClassMethodSummaries; | 
|  | 786 |  | 
|  | 787 | return getMethodSummary(S, ClsName, ID, MD, ResultTy, *CachedSummaries); | 
| Ted Kremenek | 99fe169 | 2009-04-29 17:17:48 +0000 | [diff] [blame] | 788 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 789 |  | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 790 | const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD, | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 791 | Selector S, QualType RetTy); | 
| Ted Kremenek | 223a7d5 | 2009-04-29 23:03:22 +0000 | [diff] [blame] | 792 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 793 | void updateSummaryFromAnnotations(const RetainSummary *&Summ, | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 794 | const ObjCMethodDecl *MD); | 
|  | 795 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 796 | void updateSummaryFromAnnotations(const RetainSummary *&Summ, | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 797 | const FunctionDecl *FD); | 
|  | 798 |  | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 799 | bool isGCEnabled() const { return GCEnabled; } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 800 |  | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 801 | bool isARCEnabled() const { return ARCEnabled; } | 
|  | 802 |  | 
|  | 803 | bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; } | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 804 | }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 805 |  | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 806 | // Used to avoid allocating long-term (BPAlloc'd) memory for default retain | 
|  | 807 | // summaries. If a function or method looks like it has a default summary, but | 
|  | 808 | // it has annotations, the annotations are added to the stack-based template | 
|  | 809 | // and then copied into managed memory. | 
|  | 810 | class RetainSummaryTemplate { | 
|  | 811 | RetainSummaryManager &Manager; | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 812 | const RetainSummary *&RealSummary; | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 813 | RetainSummary ScratchSummary; | 
|  | 814 | bool Accessed; | 
|  | 815 | public: | 
| Jordy Rose | 7063883 | 2012-03-17 19:53:04 +0000 | [diff] [blame] | 816 | RetainSummaryTemplate(const RetainSummary *&real, const RetainSummary &base, | 
|  | 817 | RetainSummaryManager &mgr) | 
|  | 818 | : Manager(mgr), RealSummary(real), ScratchSummary(real ? *real : base), | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 819 | Accessed(false) {} | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 820 |  | 
|  | 821 | ~RetainSummaryTemplate() { | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 822 | if (Accessed) | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 823 | RealSummary = Manager.getPersistentSummary(ScratchSummary); | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 824 | } | 
|  | 825 |  | 
|  | 826 | RetainSummary &operator*() { | 
|  | 827 | Accessed = true; | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 828 | return ScratchSummary; | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 829 | } | 
|  | 830 |  | 
|  | 831 | RetainSummary *operator->() { | 
|  | 832 | Accessed = true; | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 833 | return &ScratchSummary; | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 834 | } | 
|  | 835 | }; | 
|  | 836 |  | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 837 | } // end anonymous namespace | 
|  | 838 |  | 
|  | 839 | //===----------------------------------------------------------------------===// | 
|  | 840 | // Implementation of checker data structures. | 
|  | 841 | //===----------------------------------------------------------------------===// | 
|  | 842 |  | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 843 | ArgEffects RetainSummaryManager::getArgEffects() { | 
|  | 844 | ArgEffects AE = ScratchArgs; | 
| Ted Kremenek | b3b56c6 | 2010-11-24 00:54:37 +0000 | [diff] [blame] | 845 | ScratchArgs = AF.getEmptyMap(); | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 846 | return AE; | 
| Ted Kremenek | 68d73d1 | 2008-03-12 01:21:45 +0000 | [diff] [blame] | 847 | } | 
|  | 848 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 849 | const RetainSummary * | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 850 | RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) { | 
|  | 851 | // Unique "simple" summaries -- those without ArgEffects. | 
|  | 852 | if (OldSumm.isSimple()) { | 
|  | 853 | llvm::FoldingSetNodeID ID; | 
|  | 854 | OldSumm.Profile(ID); | 
|  | 855 |  | 
|  | 856 | void *Pos; | 
|  | 857 | CachedSummaryNode *N = SimpleSummaries.FindNodeOrInsertPos(ID, Pos); | 
|  | 858 |  | 
|  | 859 | if (!N) { | 
|  | 860 | N = (CachedSummaryNode *) BPAlloc.Allocate<CachedSummaryNode>(); | 
|  | 861 | new (N) CachedSummaryNode(OldSumm); | 
|  | 862 | SimpleSummaries.InsertNode(N, Pos); | 
|  | 863 | } | 
|  | 864 |  | 
|  | 865 | return &N->getValue(); | 
|  | 866 | } | 
|  | 867 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 868 | RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>(); | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 869 | new (Summ) RetainSummary(OldSumm); | 
| Ted Kremenek | 68d73d1 | 2008-03-12 01:21:45 +0000 | [diff] [blame] | 870 | return Summ; | 
|  | 871 | } | 
|  | 872 |  | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 873 | //===----------------------------------------------------------------------===// | 
|  | 874 | // Summary creation for functions (largely uses of Core Foundation). | 
|  | 875 | //===----------------------------------------------------------------------===// | 
| Ted Kremenek | 68d73d1 | 2008-03-12 01:21:45 +0000 | [diff] [blame] | 876 |  | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 877 | static bool isRetain(const FunctionDecl *FD, StringRef FName) { | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 878 | return FName.endswith("Retain"); | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 879 | } | 
|  | 880 |  | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 881 | static bool isRelease(const FunctionDecl *FD, StringRef FName) { | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 882 | return FName.endswith("Release"); | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 883 | } | 
|  | 884 |  | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 885 | static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) { | 
|  | 886 | // FIXME: Remove FunctionDecl parameter. | 
|  | 887 | // FIXME: Is it really okay if MakeCollectable isn't a suffix? | 
|  | 888 | return FName.find("MakeCollectable") != StringRef::npos; | 
|  | 889 | } | 
|  | 890 |  | 
| Anna Zaks | f4c5ea5 | 2012-05-04 22:18:39 +0000 | [diff] [blame] | 891 | const RetainSummary * | 
|  | 892 | RetainSummaryManager::getSummary(const FunctionDecl *FD, | 
|  | 893 | const CallOrObjCMessage *CME) { | 
| Ted Kremenek | f714159 | 2008-04-24 17:22:33 +0000 | [diff] [blame] | 894 | // Look up a summary in our cache of FunctionDecls -> Summaries. | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 895 | FuncSummariesTy::iterator I = FuncSummaries.find(FD); | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 896 | if (I != FuncSummaries.end()) | 
| Ted Kremenek | f714159 | 2008-04-24 17:22:33 +0000 | [diff] [blame] | 897 | return I->second; | 
|  | 898 |  | 
| Ted Kremenek | df76e6d | 2009-05-04 15:34:07 +0000 | [diff] [blame] | 899 | // No summary?  Generate one. | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 900 | const RetainSummary *S = 0; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 901 |  | 
| Ted Kremenek | fa89e2f | 2008-07-15 16:50:12 +0000 | [diff] [blame] | 902 | do { | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 903 | // We generate "stop" summaries for implicitly defined functions. | 
|  | 904 | if (FD->isImplicit()) { | 
|  | 905 | S = getPersistentStopSummary(); | 
|  | 906 | break; | 
| Ted Kremenek | fa89e2f | 2008-07-15 16:50:12 +0000 | [diff] [blame] | 907 | } | 
| Ted Kremenek | e9f364f | 2011-05-02 21:21:42 +0000 | [diff] [blame] | 908 | // For C++ methods, generate an implicit "stop" summary as well.  We | 
|  | 909 | // can relax this once we have a clear policy for C++ methods and | 
|  | 910 | // ownership attributes. | 
|  | 911 | if (isa<CXXMethodDecl>(FD)) { | 
|  | 912 | S = getPersistentStopSummary(); | 
|  | 913 | break; | 
|  | 914 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 915 |  | 
| John McCall | 9dd450b | 2009-09-21 23:43:11 +0000 | [diff] [blame] | 916 | // [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the | 
| Ted Kremenek | 86afde3 | 2009-01-16 18:40:33 +0000 | [diff] [blame] | 917 | // function's type. | 
| John McCall | 9dd450b | 2009-09-21 23:43:11 +0000 | [diff] [blame] | 918 | const FunctionType* FT = FD->getType()->getAs<FunctionType>(); | 
| Ted Kremenek | 9bcc264 | 2009-12-16 06:06:43 +0000 | [diff] [blame] | 919 | const IdentifierInfo *II = FD->getIdentifier(); | 
|  | 920 | if (!II) | 
|  | 921 | break; | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 922 |  | 
|  | 923 | StringRef FName = II->getName(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 924 |  | 
| Ted Kremenek | 5f96893 | 2009-03-05 22:11:14 +0000 | [diff] [blame] | 925 | // Strip away preceding '_'.  Doing this here will effect all the checks | 
|  | 926 | // down below. | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 927 | FName = FName.substr(FName.find_first_not_of('_')); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 928 |  | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 929 | // Inspect the result type. | 
|  | 930 | QualType RetTy = FT->getResultType(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 931 |  | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 932 | // FIXME: This should all be refactored into a chain of "summary lookup" | 
|  | 933 | //  filters. | 
| Ted Kremenek | b4ec3fc | 2009-10-14 00:27:24 +0000 | [diff] [blame] | 934 | assert(ScratchArgs.isEmpty()); | 
| Ted Kremenek | 3092e9c | 2009-06-15 20:36:07 +0000 | [diff] [blame] | 935 |  | 
| Ted Kremenek | 01d152f | 2012-04-26 04:32:23 +0000 | [diff] [blame] | 936 | if (FName == "pthread_create" || FName == "pthread_setspecific") { | 
|  | 937 | // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>. | 
|  | 938 | // This will be addressed better with IPA. | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 939 | S = getPersistentStopSummary(); | 
|  | 940 | } else if (FName == "NSMakeCollectable") { | 
|  | 941 | // Handle: id NSMakeCollectable(CFTypeRef) | 
|  | 942 | S = (RetTy->isObjCIdType()) | 
|  | 943 | ? getUnarySummary(FT, cfmakecollectable) | 
|  | 944 | : getPersistentStopSummary(); | 
|  | 945 | } else if (FName == "IOBSDNameMatching" || | 
|  | 946 | FName == "IOServiceMatching" || | 
|  | 947 | FName == "IOServiceNameMatching" || | 
| Ted Kremenek | 555560c | 2012-05-01 05:28:27 +0000 | [diff] [blame] | 948 | FName == "IORegistryEntrySearchCFProperty" || | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 949 | FName == "IORegistryEntryIDMatching" || | 
|  | 950 | FName == "IOOpenFirmwarePathMatching") { | 
|  | 951 | // Part of <rdar://problem/6961230>. (IOKit) | 
|  | 952 | // This should be addressed using a API table. | 
|  | 953 | S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), | 
|  | 954 | DoNothing, DoNothing); | 
|  | 955 | } else if (FName == "IOServiceGetMatchingService" || | 
|  | 956 | FName == "IOServiceGetMatchingServices") { | 
|  | 957 | // FIXES: <rdar://problem/6326900> | 
|  | 958 | // This should be addressed using a API table.  This strcmp is also | 
|  | 959 | // a little gross, but there is no need to super optimize here. | 
| Ted Kremenek | b3b56c6 | 2010-11-24 00:54:37 +0000 | [diff] [blame] | 960 | ScratchArgs = AF.add(ScratchArgs, 1, DecRef); | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 961 | S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); | 
|  | 962 | } else if (FName == "IOServiceAddNotification" || | 
|  | 963 | FName == "IOServiceAddMatchingNotification") { | 
|  | 964 | // Part of <rdar://problem/6961230>. (IOKit) | 
|  | 965 | // This should be addressed using a API table. | 
| Ted Kremenek | b3b56c6 | 2010-11-24 00:54:37 +0000 | [diff] [blame] | 966 | ScratchArgs = AF.add(ScratchArgs, 2, DecRef); | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 967 | S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); | 
|  | 968 | } else if (FName == "CVPixelBufferCreateWithBytes") { | 
|  | 969 | // FIXES: <rdar://problem/7283567> | 
|  | 970 | // Eventually this can be improved by recognizing that the pixel | 
|  | 971 | // buffer passed to CVPixelBufferCreateWithBytes is released via | 
|  | 972 | // a callback and doing full IPA to make sure this is done correctly. | 
|  | 973 | // FIXME: This function has an out parameter that returns an | 
|  | 974 | // allocated object. | 
| Ted Kremenek | b3b56c6 | 2010-11-24 00:54:37 +0000 | [diff] [blame] | 975 | ScratchArgs = AF.add(ScratchArgs, 7, StopTracking); | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 976 | S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); | 
|  | 977 | } else if (FName == "CGBitmapContextCreateWithData") { | 
|  | 978 | // FIXES: <rdar://problem/7358899> | 
|  | 979 | // Eventually this can be improved by recognizing that 'releaseInfo' | 
|  | 980 | // passed to CGBitmapContextCreateWithData is released via | 
|  | 981 | // a callback and doing full IPA to make sure this is done correctly. | 
| Ted Kremenek | b3b56c6 | 2010-11-24 00:54:37 +0000 | [diff] [blame] | 982 | ScratchArgs = AF.add(ScratchArgs, 8, StopTracking); | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 983 | S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), | 
|  | 984 | DoNothing, DoNothing); | 
|  | 985 | } else if (FName == "CVPixelBufferCreateWithPlanarBytes") { | 
|  | 986 | // FIXES: <rdar://problem/7283567> | 
|  | 987 | // Eventually this can be improved by recognizing that the pixel | 
|  | 988 | // buffer passed to CVPixelBufferCreateWithPlanarBytes is released | 
|  | 989 | // via a callback and doing full IPA to make sure this is done | 
|  | 990 | // correctly. | 
| Ted Kremenek | b3b56c6 | 2010-11-24 00:54:37 +0000 | [diff] [blame] | 991 | ScratchArgs = AF.add(ScratchArgs, 12, StopTracking); | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 992 | S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); | 
| Ted Kremenek | 40c1343 | 2012-03-22 06:29:41 +0000 | [diff] [blame] | 993 | } else if (FName == "dispatch_set_context") { | 
|  | 994 | // <rdar://problem/11059275> - The analyzer currently doesn't have | 
|  | 995 | // a good way to reason about the finalizer function for libdispatch. | 
|  | 996 | // If we pass a context object that is memory managed, stop tracking it. | 
|  | 997 | // FIXME: this hack should possibly go away once we can handle | 
|  | 998 | // libdispatch finalizers. | 
|  | 999 | ScratchArgs = AF.add(ScratchArgs, 1, StopTracking); | 
|  | 1000 | S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); | 
| Ted Kremenek | ececf9f | 2012-05-08 00:12:09 +0000 | [diff] [blame] | 1001 | } else if (FName.startswith("NSLog")) { | 
|  | 1002 | S = getDoNothingSummary(); | 
| Anna Zaks | 90ab9bf | 2012-03-30 05:48:16 +0000 | [diff] [blame] | 1003 | } else if (FName.startswith("NS") && | 
|  | 1004 | (FName.find("Insert") != StringRef::npos)) { | 
|  | 1005 | // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can | 
|  | 1006 | // be deallocated by NSMapRemove. (radar://11152419) | 
|  | 1007 | ScratchArgs = AF.add(ScratchArgs, 1, StopTracking); | 
|  | 1008 | ScratchArgs = AF.add(ScratchArgs, 2, StopTracking); | 
|  | 1009 | S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); | 
| Anna Zaks | f4c5ea5 | 2012-05-04 22:18:39 +0000 | [diff] [blame] | 1010 | } else if (CME && CME->hasNonZeroCallbackArg()) { | 
| Anna Zaks | 41e7ab8 | 2012-05-07 17:47:09 +0000 | [diff] [blame] | 1011 | // Allow objects to escape through callbacks. radar://10973977 | 
|  | 1012 | S = getPersistentStopSummary(); | 
| Ted Kremenek | ea675cf | 2009-06-11 18:17:24 +0000 | [diff] [blame] | 1013 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1014 |  | 
| Ted Kremenek | ea675cf | 2009-06-11 18:17:24 +0000 | [diff] [blame] | 1015 | // Did we get a summary? | 
|  | 1016 | if (S) | 
|  | 1017 | break; | 
| Ted Kremenek | 211094d | 2009-03-17 22:43:44 +0000 | [diff] [blame] | 1018 |  | 
|  | 1019 | // Enable this code once the semantics of NSDeallocateObject are resolved | 
|  | 1020 | // for GC.  <rdar://problem/6619988> | 
|  | 1021 | #if 0 | 
|  | 1022 | // Handle: NSDeallocateObject(id anObject); | 
|  | 1023 | // This method does allow 'nil' (although we don't check it now). | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1024 | if (strcmp(FName, "NSDeallocateObject") == 0) { | 
| Ted Kremenek | 211094d | 2009-03-17 22:43:44 +0000 | [diff] [blame] | 1025 | return RetTy == Ctx.VoidTy | 
|  | 1026 | ? getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Dealloc) | 
|  | 1027 | : getPersistentStopSummary(); | 
|  | 1028 | } | 
|  | 1029 | #endif | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1030 |  | 
|  | 1031 | if (RetTy->isPointerType()) { | 
|  | 1032 | // For CoreFoundation ('CF') types. | 
| Ted Kremenek | e991835 | 2010-01-27 18:00:17 +0000 | [diff] [blame] | 1033 | if (cocoa::isRefType(RetTy, "CF", FName)) { | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1034 | if (isRetain(FD, FName)) | 
|  | 1035 | S = getUnarySummary(FT, cfretain); | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 1036 | else if (isMakeCollectable(FD, FName)) | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1037 | S = getUnarySummary(FT, cfmakecollectable); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1038 | else | 
| John McCall | 525f055 | 2011-10-01 00:48:56 +0000 | [diff] [blame] | 1039 | S = getCFCreateGetRuleSummary(FD); | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1040 |  | 
|  | 1041 | break; | 
|  | 1042 | } | 
|  | 1043 |  | 
|  | 1044 | // For CoreGraphics ('CG') types. | 
| Ted Kremenek | e991835 | 2010-01-27 18:00:17 +0000 | [diff] [blame] | 1045 | if (cocoa::isRefType(RetTy, "CG", FName)) { | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1046 | if (isRetain(FD, FName)) | 
|  | 1047 | S = getUnarySummary(FT, cfretain); | 
|  | 1048 | else | 
| John McCall | 525f055 | 2011-10-01 00:48:56 +0000 | [diff] [blame] | 1049 | S = getCFCreateGetRuleSummary(FD); | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1050 |  | 
|  | 1051 | break; | 
|  | 1052 | } | 
|  | 1053 |  | 
|  | 1054 | // For the Disk Arbitration API (DiskArbitration/DADisk.h) | 
| Ted Kremenek | e991835 | 2010-01-27 18:00:17 +0000 | [diff] [blame] | 1055 | if (cocoa::isRefType(RetTy, "DADisk") || | 
|  | 1056 | cocoa::isRefType(RetTy, "DADissenter") || | 
|  | 1057 | cocoa::isRefType(RetTy, "DASessionRef")) { | 
| John McCall | 525f055 | 2011-10-01 00:48:56 +0000 | [diff] [blame] | 1058 | S = getCFCreateGetRuleSummary(FD); | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1059 | break; | 
|  | 1060 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1061 |  | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1062 | break; | 
|  | 1063 | } | 
|  | 1064 |  | 
|  | 1065 | // Check for release functions, the only kind of functions that we care | 
|  | 1066 | // about that don't return a pointer type. | 
|  | 1067 | if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G')) { | 
| Ted Kremenek | ac5ab79 | 2010-02-08 16:45:01 +0000 | [diff] [blame] | 1068 | // Test for 'CGCF'. | 
| Benjamin Kramer | eaabbd8 | 2010-02-08 18:38:55 +0000 | [diff] [blame] | 1069 | FName = FName.substr(FName.startswith("CGCF") ? 4 : 2); | 
| Ted Kremenek | ac5ab79 | 2010-02-08 16:45:01 +0000 | [diff] [blame] | 1070 |  | 
| Ted Kremenek | 5f96893 | 2009-03-05 22:11:14 +0000 | [diff] [blame] | 1071 | if (isRelease(FD, FName)) | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1072 | S = getUnarySummary(FT, cfrelease); | 
|  | 1073 | else { | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 1074 | assert (ScratchArgs.isEmpty()); | 
| Ted Kremenek | ed90de4 | 2009-01-29 22:45:13 +0000 | [diff] [blame] | 1075 | // Remaining CoreFoundation and CoreGraphics functions. | 
|  | 1076 | // We use to assume that they all strictly followed the ownership idiom | 
|  | 1077 | // and that ownership cannot be transferred.  While this is technically | 
|  | 1078 | // correct, many methods allow a tracked object to escape.  For example: | 
|  | 1079 | // | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1080 | //   CFMutableDictionaryRef x = CFDictionaryCreateMutable(...); | 
| Ted Kremenek | ed90de4 | 2009-01-29 22:45:13 +0000 | [diff] [blame] | 1081 | //   CFDictionaryAddValue(y, key, x); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1082 | //   CFRelease(x); | 
| Ted Kremenek | ed90de4 | 2009-01-29 22:45:13 +0000 | [diff] [blame] | 1083 | //   ... it is okay to use 'x' since 'y' has a reference to it | 
|  | 1084 | // | 
|  | 1085 | // We handle this and similar cases with the follow heuristic.  If the | 
| Ted Kremenek | d982f00 | 2009-08-20 00:57:22 +0000 | [diff] [blame] | 1086 | // function name contains "InsertValue", "SetValue", "AddValue", | 
|  | 1087 | // "AppendValue", or "SetAttribute", then we assume that arguments may | 
|  | 1088 | // "escape."  This means that something else holds on to the object, | 
|  | 1089 | // allowing it be used even after its local retain count drops to 0. | 
| Benjamin Kramer | 0129bd7 | 2010-01-11 19:46:28 +0000 | [diff] [blame] | 1090 | ArgEffect E = (StrInStrNoCase(FName, "InsertValue") != StringRef::npos|| | 
|  | 1091 | StrInStrNoCase(FName, "AddValue") != StringRef::npos || | 
|  | 1092 | StrInStrNoCase(FName, "SetValue") != StringRef::npos || | 
|  | 1093 | StrInStrNoCase(FName, "AppendValue") != StringRef::npos|| | 
| Benjamin Kramer | 3780831 | 2010-01-11 20:15:06 +0000 | [diff] [blame] | 1094 | StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) | 
| Ted Kremenek | ed90de4 | 2009-01-29 22:45:13 +0000 | [diff] [blame] | 1095 | ? MayEscape : DoNothing; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1096 |  | 
| Ted Kremenek | ed90de4 | 2009-01-29 22:45:13 +0000 | [diff] [blame] | 1097 | S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, E); | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1098 | } | 
|  | 1099 | } | 
| Ted Kremenek | fa89e2f | 2008-07-15 16:50:12 +0000 | [diff] [blame] | 1100 | } | 
|  | 1101 | while (0); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1102 |  | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 1103 | // Annotations override defaults. | 
| Jordy Rose | 212e459 | 2011-08-23 04:27:15 +0000 | [diff] [blame] | 1104 | updateSummaryFromAnnotations(S, FD); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1105 |  | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 1106 | FuncSummaries[FD] = S; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1107 | return S; | 
| Ted Kremenek | ea6507f | 2008-03-06 00:08:09 +0000 | [diff] [blame] | 1108 | } | 
|  | 1109 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1110 | const RetainSummary * | 
| John McCall | 525f055 | 2011-10-01 00:48:56 +0000 | [diff] [blame] | 1111 | RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) { | 
|  | 1112 | if (coreFoundation::followsCreateRule(FD)) | 
| Ted Kremenek | 875db81 | 2008-05-05 16:51:50 +0000 | [diff] [blame] | 1113 | return getCFSummaryCreateRule(FD); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1114 |  | 
| Ted Kremenek | 8e2c9b0 | 2011-05-25 06:19:45 +0000 | [diff] [blame] | 1115 | return getCFSummaryGetRule(FD); | 
| Ted Kremenek | 875db81 | 2008-05-05 16:51:50 +0000 | [diff] [blame] | 1116 | } | 
|  | 1117 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1118 | const RetainSummary * | 
| Ted Kremenek | 82157a1 | 2009-02-23 16:51:39 +0000 | [diff] [blame] | 1119 | RetainSummaryManager::getUnarySummary(const FunctionType* FT, | 
|  | 1120 | UnaryFuncKind func) { | 
|  | 1121 |  | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1122 | // Sanity check that this is *really* a unary function.  This can | 
|  | 1123 | // happen if people do weird things. | 
| Douglas Gregor | deaad8c | 2009-02-26 23:50:07 +0000 | [diff] [blame] | 1124 | const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT); | 
| Ted Kremenek | 7e90422 | 2009-01-12 21:45:02 +0000 | [diff] [blame] | 1125 | if (!FTP || FTP->getNumArgs() != 1) | 
|  | 1126 | return getPersistentStopSummary(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1127 |  | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 1128 | assert (ScratchArgs.isEmpty()); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1129 |  | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 1130 | ArgEffect Effect; | 
| Ted Kremenek | 4b7ca77 | 2008-04-29 05:33:51 +0000 | [diff] [blame] | 1131 | switch (func) { | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 1132 | case cfretain: Effect = IncRef; break; | 
|  | 1133 | case cfrelease: Effect = DecRef; break; | 
|  | 1134 | case cfmakecollectable: Effect = MakeCollectable; break; | 
| Ted Kremenek | 4b77209 | 2008-04-10 23:44:06 +0000 | [diff] [blame] | 1135 | } | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 1136 |  | 
|  | 1137 | ScratchArgs = AF.add(ScratchArgs, 0, Effect); | 
|  | 1138 | return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); | 
| Ted Kremenek | 68d73d1 | 2008-03-12 01:21:45 +0000 | [diff] [blame] | 1139 | } | 
|  | 1140 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1141 | const RetainSummary * | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 1142 | RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) { | 
| Ted Kremenek | 7d79a5f | 2009-05-03 05:20:50 +0000 | [diff] [blame] | 1143 | assert (ScratchArgs.isEmpty()); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1144 |  | 
| Ted Kremenek | aeb115f | 2009-01-28 05:56:51 +0000 | [diff] [blame] | 1145 | return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); | 
| Ted Kremenek | 68d73d1 | 2008-03-12 01:21:45 +0000 | [diff] [blame] | 1146 | } | 
|  | 1147 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1148 | const RetainSummary * | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 1149 | RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) { | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1150 | assert (ScratchArgs.isEmpty()); | 
| Ted Kremenek | aeb115f | 2009-01-28 05:56:51 +0000 | [diff] [blame] | 1151 | return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF), | 
|  | 1152 | DoNothing, DoNothing); | 
| Ted Kremenek | 68d73d1 | 2008-03-12 01:21:45 +0000 | [diff] [blame] | 1153 | } | 
|  | 1154 |  | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 1155 | //===----------------------------------------------------------------------===// | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 1156 | // Summary creation for Selectors. | 
|  | 1157 | //===----------------------------------------------------------------------===// | 
|  | 1158 |  | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 1159 | void | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1160 | RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 1161 | const FunctionDecl *FD) { | 
|  | 1162 | if (!FD) | 
|  | 1163 | return; | 
|  | 1164 |  | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 1165 | RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this); | 
| Jordy Rose | 212e459 | 2011-08-23 04:27:15 +0000 | [diff] [blame] | 1166 |  | 
| Ted Kremenek | afe348e | 2011-01-27 18:43:03 +0000 | [diff] [blame] | 1167 | // Effects on the parameters. | 
|  | 1168 | unsigned parm_idx = 0; | 
|  | 1169 | for (FunctionDecl::param_const_iterator pi = FD->param_begin(), | 
| John McCall | 3337ca5 | 2011-04-06 09:02:12 +0000 | [diff] [blame] | 1170 | pe = FD->param_end(); pi != pe; ++pi, ++parm_idx) { | 
| Ted Kremenek | afe348e | 2011-01-27 18:43:03 +0000 | [diff] [blame] | 1171 | const ParmVarDecl *pd = *pi; | 
|  | 1172 | if (pd->getAttr<NSConsumedAttr>()) { | 
| Jordy Rose | 212e459 | 2011-08-23 04:27:15 +0000 | [diff] [blame] | 1173 | if (!GCEnabled) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1174 | Template->addArg(AF, parm_idx, DecRef); | 
| Jordy Rose | 212e459 | 2011-08-23 04:27:15 +0000 | [diff] [blame] | 1175 | } | 
|  | 1176 | } else if (pd->getAttr<CFConsumedAttr>()) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1177 | Template->addArg(AF, parm_idx, DecRef); | 
| Ted Kremenek | afe348e | 2011-01-27 18:43:03 +0000 | [diff] [blame] | 1178 | } | 
|  | 1179 | } | 
|  | 1180 |  | 
| Ted Kremenek | ea675cf | 2009-06-11 18:17:24 +0000 | [diff] [blame] | 1181 | QualType RetTy = FD->getResultType(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1182 |  | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 1183 | // Determine if there is a special return effect for this method. | 
| Ted Kremenek | e991835 | 2010-01-27 18:00:17 +0000 | [diff] [blame] | 1184 | if (cocoa::isCocoaObjectRef(RetTy)) { | 
| Argyrios Kyrtzidis | b4b64ca | 2009-06-30 02:34:44 +0000 | [diff] [blame] | 1185 | if (FD->getAttr<NSReturnsRetainedAttr>()) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1186 | Template->setRetEffect(ObjCAllocRetE); | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 1187 | } | 
| Argyrios Kyrtzidis | b4b64ca | 2009-06-30 02:34:44 +0000 | [diff] [blame] | 1188 | else if (FD->getAttr<CFReturnsRetainedAttr>()) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1189 | Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); | 
| Ted Kremenek | ea675cf | 2009-06-11 18:17:24 +0000 | [diff] [blame] | 1190 | } | 
| Ted Kremenek | 1fcc56c | 2010-02-18 00:06:12 +0000 | [diff] [blame] | 1191 | else if (FD->getAttr<NSReturnsNotRetainedAttr>()) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1192 | Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); | 
| Ted Kremenek | 1fcc56c | 2010-02-18 00:06:12 +0000 | [diff] [blame] | 1193 | } | 
|  | 1194 | else if (FD->getAttr<CFReturnsNotRetainedAttr>()) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1195 | Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); | 
| Jordy Rose | 212e459 | 2011-08-23 04:27:15 +0000 | [diff] [blame] | 1196 | } | 
|  | 1197 | } else if (RetTy->getAs<PointerType>()) { | 
|  | 1198 | if (FD->getAttr<CFReturnsRetainedAttr>()) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1199 | Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); | 
| Jordy Rose | 212e459 | 2011-08-23 04:27:15 +0000 | [diff] [blame] | 1200 | } | 
|  | 1201 | else if (FD->getAttr<CFReturnsNotRetainedAttr>()) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1202 | Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); | 
| Ted Kremenek | 1fcc56c | 2010-02-18 00:06:12 +0000 | [diff] [blame] | 1203 | } | 
| Ted Kremenek | ea675cf | 2009-06-11 18:17:24 +0000 | [diff] [blame] | 1204 | } | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 1205 | } | 
|  | 1206 |  | 
|  | 1207 | void | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1208 | RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ, | 
|  | 1209 | const ObjCMethodDecl *MD) { | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 1210 | if (!MD) | 
|  | 1211 | return; | 
|  | 1212 |  | 
| Jordy Rose | 61c974b | 2012-03-18 01:26:10 +0000 | [diff] [blame] | 1213 | RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this); | 
| Ted Kremenek | 0578e43 | 2009-07-06 18:30:43 +0000 | [diff] [blame] | 1214 | bool isTrackedLoc = false; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1215 |  | 
| Ted Kremenek | 0e89838 | 2011-01-27 06:54:14 +0000 | [diff] [blame] | 1216 | // Effects on the receiver. | 
|  | 1217 | if (MD->getAttr<NSConsumesSelfAttr>()) { | 
| Ted Kremenek | afe348e | 2011-01-27 18:43:03 +0000 | [diff] [blame] | 1218 | if (!GCEnabled) | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1219 | Template->setReceiverEffect(DecRefMsg); | 
| Ted Kremenek | afe348e | 2011-01-27 18:43:03 +0000 | [diff] [blame] | 1220 | } | 
|  | 1221 |  | 
|  | 1222 | // Effects on the parameters. | 
|  | 1223 | unsigned parm_idx = 0; | 
| Argyrios Kyrtzidis | b8c3aaf | 2011-10-03 06:37:04 +0000 | [diff] [blame] | 1224 | for (ObjCMethodDecl::param_const_iterator | 
|  | 1225 | pi=MD->param_begin(), pe=MD->param_end(); | 
| Ted Kremenek | afe348e | 2011-01-27 18:43:03 +0000 | [diff] [blame] | 1226 | pi != pe; ++pi, ++parm_idx) { | 
|  | 1227 | const ParmVarDecl *pd = *pi; | 
|  | 1228 | if (pd->getAttr<NSConsumedAttr>()) { | 
|  | 1229 | if (!GCEnabled) | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1230 | Template->addArg(AF, parm_idx, DecRef); | 
| Ted Kremenek | afe348e | 2011-01-27 18:43:03 +0000 | [diff] [blame] | 1231 | } | 
|  | 1232 | else if(pd->getAttr<CFConsumedAttr>()) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1233 | Template->addArg(AF, parm_idx, DecRef); | 
| Ted Kremenek | afe348e | 2011-01-27 18:43:03 +0000 | [diff] [blame] | 1234 | } | 
| Ted Kremenek | 0e89838 | 2011-01-27 06:54:14 +0000 | [diff] [blame] | 1235 | } | 
|  | 1236 |  | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 1237 | // Determine if there is a special return effect for this method. | 
| Ted Kremenek | e991835 | 2010-01-27 18:00:17 +0000 | [diff] [blame] | 1238 | if (cocoa::isCocoaObjectRef(MD->getResultType())) { | 
| Argyrios Kyrtzidis | b4b64ca | 2009-06-30 02:34:44 +0000 | [diff] [blame] | 1239 | if (MD->getAttr<NSReturnsRetainedAttr>()) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1240 | Template->setRetEffect(ObjCAllocRetE); | 
| Ted Kremenek | 0578e43 | 2009-07-06 18:30:43 +0000 | [diff] [blame] | 1241 | return; | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 1242 | } | 
| Ted Kremenek | 1fcc56c | 2010-02-18 00:06:12 +0000 | [diff] [blame] | 1243 | if (MD->getAttr<NSReturnsNotRetainedAttr>()) { | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1244 | Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); | 
| Ted Kremenek | 1fcc56c | 2010-02-18 00:06:12 +0000 | [diff] [blame] | 1245 | return; | 
|  | 1246 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1247 |  | 
| Ted Kremenek | 0578e43 | 2009-07-06 18:30:43 +0000 | [diff] [blame] | 1248 | isTrackedLoc = true; | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1249 | } else { | 
| Ted Kremenek | c23c7e6 | 2009-07-29 21:53:49 +0000 | [diff] [blame] | 1250 | isTrackedLoc = MD->getResultType()->getAs<PointerType>() != NULL; | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1251 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1252 |  | 
| Ted Kremenek | 1fcc56c | 2010-02-18 00:06:12 +0000 | [diff] [blame] | 1253 | if (isTrackedLoc) { | 
|  | 1254 | if (MD->getAttr<CFReturnsRetainedAttr>()) | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1255 | Template->setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); | 
| Ted Kremenek | 1fcc56c | 2010-02-18 00:06:12 +0000 | [diff] [blame] | 1256 | else if (MD->getAttr<CFReturnsNotRetainedAttr>()) | 
| Jordy Rose | 14de7c5 | 2011-08-24 09:02:37 +0000 | [diff] [blame] | 1257 | Template->setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); | 
| Ted Kremenek | 1fcc56c | 2010-02-18 00:06:12 +0000 | [diff] [blame] | 1258 | } | 
| Ted Kremenek | c2de727 | 2009-05-09 02:58:13 +0000 | [diff] [blame] | 1259 | } | 
|  | 1260 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1261 | const RetainSummary * | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 1262 | RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD, | 
|  | 1263 | Selector S, QualType RetTy) { | 
| Ted Kremenek | 6a966b2 | 2009-04-24 21:56:17 +0000 | [diff] [blame] | 1264 |  | 
| Ted Kremenek | 7686ffa | 2009-04-29 00:42:39 +0000 | [diff] [blame] | 1265 | if (MD) { | 
| Ted Kremenek | 6e86caf | 2009-04-24 18:00:17 +0000 | [diff] [blame] | 1266 | // Scan the method decl for 'void*' arguments.  These should be treated | 
|  | 1267 | // as 'StopTracking' because they are often used with delegates. | 
|  | 1268 | // Delegates are a frequent form of false positives with the retain | 
|  | 1269 | // count checker. | 
|  | 1270 | unsigned i = 0; | 
| Argyrios Kyrtzidis | b8c3aaf | 2011-10-03 06:37:04 +0000 | [diff] [blame] | 1271 | for (ObjCMethodDecl::param_const_iterator I = MD->param_begin(), | 
| Ted Kremenek | 6e86caf | 2009-04-24 18:00:17 +0000 | [diff] [blame] | 1272 | E = MD->param_end(); I != E; ++I, ++i) | 
| Argyrios Kyrtzidis | b8c3aaf | 2011-10-03 06:37:04 +0000 | [diff] [blame] | 1273 | if (const ParmVarDecl *PD = *I) { | 
| Ted Kremenek | 6e86caf | 2009-04-24 18:00:17 +0000 | [diff] [blame] | 1274 | QualType Ty = Ctx.getCanonicalType(PD->getType()); | 
| Douglas Gregor | 1b8fe5b7 | 2009-11-16 21:35:15 +0000 | [diff] [blame] | 1275 | if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy) | 
| Ted Kremenek | b3b56c6 | 2010-11-24 00:54:37 +0000 | [diff] [blame] | 1276 | ScratchArgs = AF.add(ScratchArgs, i, StopTracking); | 
| Ted Kremenek | 6e86caf | 2009-04-24 18:00:17 +0000 | [diff] [blame] | 1277 | } | 
|  | 1278 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1279 |  | 
| Jordy Rose | 7063883 | 2012-03-17 19:53:04 +0000 | [diff] [blame] | 1280 | // Any special effects? | 
| Ted Kremenek | 6a966b2 | 2009-04-24 21:56:17 +0000 | [diff] [blame] | 1281 | ArgEffect ReceiverEff = DoNothing; | 
| Jordy Rose | 7063883 | 2012-03-17 19:53:04 +0000 | [diff] [blame] | 1282 | RetEffect ResultEff = RetEffect::MakeNoRet(); | 
|  | 1283 |  | 
|  | 1284 | // Check the method family, and apply any default annotations. | 
|  | 1285 | switch (MD ? MD->getMethodFamily() : S.getMethodFamily()) { | 
|  | 1286 | case OMF_None: | 
|  | 1287 | case OMF_performSelector: | 
|  | 1288 | // Assume all Objective-C methods follow Cocoa Memory Management rules. | 
|  | 1289 | // FIXME: Does the non-threaded performSelector family really belong here? | 
|  | 1290 | // The selector could be, say, @selector(copy). | 
|  | 1291 | if (cocoa::isCocoaObjectRef(RetTy)) | 
|  | 1292 | ResultEff = RetEffect::MakeNotOwned(RetEffect::ObjC); | 
|  | 1293 | else if (coreFoundation::isCFObjectRef(RetTy)) { | 
|  | 1294 | // ObjCMethodDecl currently doesn't consider CF objects as valid return | 
|  | 1295 | // values for alloc, new, copy, or mutableCopy, so we have to | 
|  | 1296 | // double-check with the selector. This is ugly, but there aren't that | 
|  | 1297 | // many Objective-C methods that return CF objects, right? | 
|  | 1298 | if (MD) { | 
|  | 1299 | switch (S.getMethodFamily()) { | 
|  | 1300 | case OMF_alloc: | 
|  | 1301 | case OMF_new: | 
|  | 1302 | case OMF_copy: | 
|  | 1303 | case OMF_mutableCopy: | 
|  | 1304 | ResultEff = RetEffect::MakeOwned(RetEffect::CF, true); | 
|  | 1305 | break; | 
|  | 1306 | default: | 
|  | 1307 | ResultEff = RetEffect::MakeNotOwned(RetEffect::CF); | 
|  | 1308 | break; | 
|  | 1309 | } | 
|  | 1310 | } else { | 
|  | 1311 | ResultEff = RetEffect::MakeNotOwned(RetEffect::CF); | 
|  | 1312 | } | 
|  | 1313 | } | 
|  | 1314 | break; | 
|  | 1315 | case OMF_init: | 
|  | 1316 | ResultEff = ObjCInitRetE; | 
|  | 1317 | ReceiverEff = DecRefMsg; | 
|  | 1318 | break; | 
|  | 1319 | case OMF_alloc: | 
|  | 1320 | case OMF_new: | 
|  | 1321 | case OMF_copy: | 
|  | 1322 | case OMF_mutableCopy: | 
|  | 1323 | if (cocoa::isCocoaObjectRef(RetTy)) | 
|  | 1324 | ResultEff = ObjCAllocRetE; | 
|  | 1325 | else if (coreFoundation::isCFObjectRef(RetTy)) | 
|  | 1326 | ResultEff = RetEffect::MakeOwned(RetEffect::CF, true); | 
|  | 1327 | break; | 
|  | 1328 | case OMF_autorelease: | 
|  | 1329 | ReceiverEff = Autorelease; | 
|  | 1330 | break; | 
|  | 1331 | case OMF_retain: | 
|  | 1332 | ReceiverEff = IncRefMsg; | 
|  | 1333 | break; | 
|  | 1334 | case OMF_release: | 
|  | 1335 | ReceiverEff = DecRefMsg; | 
|  | 1336 | break; | 
|  | 1337 | case OMF_dealloc: | 
|  | 1338 | ReceiverEff = Dealloc; | 
|  | 1339 | break; | 
|  | 1340 | case OMF_self: | 
|  | 1341 | // -self is handled specially by the ExprEngine to propagate the receiver. | 
|  | 1342 | break; | 
|  | 1343 | case OMF_retainCount: | 
|  | 1344 | case OMF_finalize: | 
|  | 1345 | // These methods don't return objects. | 
|  | 1346 | break; | 
|  | 1347 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1348 |  | 
| Ted Kremenek | 6a966b2 | 2009-04-24 21:56:17 +0000 | [diff] [blame] | 1349 | // If one of the arguments in the selector has the keyword 'delegate' we | 
|  | 1350 | // should stop tracking the reference count for the receiver.  This is | 
|  | 1351 | // because the reference count is quite possibly handled by a delegate | 
|  | 1352 | // method. | 
|  | 1353 | if (S.isKeywordSelector()) { | 
|  | 1354 | const std::string &str = S.getAsString(); | 
|  | 1355 | assert(!str.empty()); | 
| Benjamin Kramer | 0129bd7 | 2010-01-11 19:46:28 +0000 | [diff] [blame] | 1356 | if (StrInStrNoCase(str, "delegate:") != StringRef::npos) | 
|  | 1357 | ReceiverEff = StopTracking; | 
| Ted Kremenek | 6a966b2 | 2009-04-24 21:56:17 +0000 | [diff] [blame] | 1358 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1359 |  | 
| Jordy Rose | 7063883 | 2012-03-17 19:53:04 +0000 | [diff] [blame] | 1360 | if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing && | 
|  | 1361 | ResultEff.getKind() == RetEffect::NoRet) | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1362 | return getDefaultSummary(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1363 |  | 
| Jordy Rose | 7063883 | 2012-03-17 19:53:04 +0000 | [diff] [blame] | 1364 | return getPersistentSummary(ResultEff, ReceiverEff, MayEscape); | 
| Ted Kremenek | 60746a0 | 2009-04-23 23:08:22 +0000 | [diff] [blame] | 1365 | } | 
|  | 1366 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1367 | const RetainSummary * | 
| Argyrios Kyrtzidis | 37ab726 | 2011-01-25 00:03:53 +0000 | [diff] [blame] | 1368 | RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg, | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 1369 | ProgramStateRef state, | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 1370 | const LocationContext *LC) { | 
|  | 1371 |  | 
|  | 1372 | // We need the type-information of the tracked receiver object | 
|  | 1373 | // Retrieve it from the state. | 
| Argyrios Kyrtzidis | 37ab726 | 2011-01-25 00:03:53 +0000 | [diff] [blame] | 1374 | const Expr *Receiver = msg.getInstanceReceiver(); | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 1375 | const ObjCInterfaceDecl *ID = 0; | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 1376 |  | 
|  | 1377 | // FIXME: Is this really working as expected?  There are cases where | 
|  | 1378 | //  we just use the 'ID' from the message expression. | 
| Douglas Gregor | 9a12919 | 2010-04-21 00:45:42 +0000 | [diff] [blame] | 1379 | SVal receiverV; | 
|  | 1380 |  | 
| Ted Kremenek | 5f03be9 | 2010-05-21 21:56:53 +0000 | [diff] [blame] | 1381 | if (Receiver) { | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 1382 | receiverV = state->getSValAsScalarOrLoc(Receiver, LC); | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 1383 |  | 
| Douglas Gregor | 9a12919 | 2010-04-21 00:45:42 +0000 | [diff] [blame] | 1384 | // FIXME: Eventually replace the use of state->get<RefBindings> with | 
|  | 1385 | // a generic API for reasoning about the Objective-C types of symbolic | 
|  | 1386 | // objects. | 
|  | 1387 | if (SymbolRef Sym = receiverV.getAsLocSymbol()) | 
|  | 1388 | if (const RefVal *T = state->get<RefBindings>(Sym)) | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 1389 | if (const ObjCObjectPointerType* PT = | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 1390 | T->getType()->getAs<ObjCObjectPointerType>()) | 
| Douglas Gregor | 9a12919 | 2010-04-21 00:45:42 +0000 | [diff] [blame] | 1391 | ID = PT->getInterfaceDecl(); | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 1392 |  | 
| Douglas Gregor | 9a12919 | 2010-04-21 00:45:42 +0000 | [diff] [blame] | 1393 | // FIXME: this is a hack.  This may or may not be the actual method | 
|  | 1394 | //  that is called. | 
|  | 1395 | if (!ID) { | 
|  | 1396 | if (const ObjCObjectPointerType *PT = | 
|  | 1397 | Receiver->getType()->getAs<ObjCObjectPointerType>()) | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 1398 | ID = PT->getInterfaceDecl(); | 
| Douglas Gregor | 9a12919 | 2010-04-21 00:45:42 +0000 | [diff] [blame] | 1399 | } | 
|  | 1400 | } else { | 
|  | 1401 | // FIXME: Hack for 'super'. | 
| Argyrios Kyrtzidis | 37ab726 | 2011-01-25 00:03:53 +0000 | [diff] [blame] | 1402 | ID = msg.getReceiverInterface(); | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 1403 | } | 
| Douglas Gregor | 9a12919 | 2010-04-21 00:45:42 +0000 | [diff] [blame] | 1404 |  | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 1405 | // FIXME: The receiver could be a reference to a class, meaning that | 
|  | 1406 | //  we should use the class method. | 
| Jordy Rose | 212e459 | 2011-08-23 04:27:15 +0000 | [diff] [blame] | 1407 | return getInstanceMethodSummary(msg, ID); | 
| Ted Kremenek | a2968e5 | 2009-11-13 01:54:21 +0000 | [diff] [blame] | 1408 | } | 
|  | 1409 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1410 | const RetainSummary * | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 1411 | RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName, | 
|  | 1412 | const ObjCInterfaceDecl *ID, | 
|  | 1413 | const ObjCMethodDecl *MD, QualType RetTy, | 
|  | 1414 | ObjCMethodSummariesTy &CachedSummaries) { | 
| Ted Kremenek | cb2e636 | 2008-05-06 15:44:25 +0000 | [diff] [blame] | 1415 |  | 
| Ted Kremenek | 0b50fb1 | 2009-04-29 05:04:30 +0000 | [diff] [blame] | 1416 | // Look up a summary in our summary cache. | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 1417 | const RetainSummary *Summ = CachedSummaries.find(ID, ClsName, S); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1418 |  | 
| Ted Kremenek | 8be5138 | 2009-07-21 23:27:57 +0000 | [diff] [blame] | 1419 | if (!Summ) { | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 1420 | Summ = getStandardMethodSummary(MD, S, RetTy); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1421 |  | 
| Ted Kremenek | 8be5138 | 2009-07-21 23:27:57 +0000 | [diff] [blame] | 1422 | // Annotations override defaults. | 
| Jordy Rose | 212e459 | 2011-08-23 04:27:15 +0000 | [diff] [blame] | 1423 | updateSummaryFromAnnotations(Summ, MD); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1424 |  | 
| Ted Kremenek | 8be5138 | 2009-07-21 23:27:57 +0000 | [diff] [blame] | 1425 | // Memoize the summary. | 
| Jordy Rose | 35e71c7 | 2012-03-17 21:13:07 +0000 | [diff] [blame] | 1426 | CachedSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; | 
| Ted Kremenek | 8be5138 | 2009-07-21 23:27:57 +0000 | [diff] [blame] | 1427 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1428 |  | 
| Ted Kremenek | f27110f | 2009-04-23 19:11:35 +0000 | [diff] [blame] | 1429 | return Summ; | 
| Ted Kremenek | 767d074 | 2008-05-06 21:26:51 +0000 | [diff] [blame] | 1430 | } | 
|  | 1431 |  | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1432 | void RetainSummaryManager::InitializeClassMethodSummaries() { | 
| Ted Kremenek | 9157fbb | 2009-05-07 23:40:42 +0000 | [diff] [blame] | 1433 | assert(ScratchArgs.isEmpty()); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1434 | // Create the [NSAssertionHandler currentHander] summary. | 
| Ted Kremenek | 55adb82 | 2009-10-15 22:25:12 +0000 | [diff] [blame] | 1435 | addClassMethSummary("NSAssertionHandler", "currentHandler", | 
| Ted Kremenek | aeb115f | 2009-01-28 05:56:51 +0000 | [diff] [blame] | 1436 | getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC))); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1437 |  | 
| Ted Kremenek | 0747e7e | 2008-10-21 15:53:15 +0000 | [diff] [blame] | 1438 | // Create the [NSAutoreleasePool addObject:] summary. | 
| Ted Kremenek | b3b56c6 | 2010-11-24 00:54:37 +0000 | [diff] [blame] | 1439 | ScratchArgs = AF.add(ScratchArgs, 0, Autorelease); | 
| Ted Kremenek | 55adb82 | 2009-10-15 22:25:12 +0000 | [diff] [blame] | 1440 | addClassMethSummary("NSAutoreleasePool", "addObject", | 
|  | 1441 | getPersistentSummary(RetEffect::MakeNoRet(), | 
|  | 1442 | DoNothing, Autorelease)); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1443 |  | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 1444 | // Create the summaries for [NSObject performSelector...].  We treat | 
|  | 1445 | // these as 'stop tracking' for the arguments because they are often | 
|  | 1446 | // used for delegates that can release the object.  When we have better | 
|  | 1447 | // inter-procedural analysis we can potentially do something better.  This | 
|  | 1448 | // workaround is to remove false positives. | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1449 | const RetainSummary *Summ = | 
| Ted Kremenek | c14efa7 | 2011-08-17 21:04:19 +0000 | [diff] [blame] | 1450 | getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking); | 
| Ted Kremenek | 8a5ad39 | 2009-04-24 17:50:11 +0000 | [diff] [blame] | 1451 | IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject"); | 
|  | 1452 | addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", | 
|  | 1453 | "afterDelay", NULL); | 
|  | 1454 | addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", | 
|  | 1455 | "afterDelay", "inModes", NULL); | 
|  | 1456 | addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", | 
|  | 1457 | "withObject", "waitUntilDone", NULL); | 
|  | 1458 | addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", | 
|  | 1459 | "withObject", "waitUntilDone", "modes", NULL); | 
|  | 1460 | addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", | 
|  | 1461 | "withObject", "waitUntilDone", NULL); | 
|  | 1462 | addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", | 
|  | 1463 | "withObject", "waitUntilDone", "modes", NULL); | 
|  | 1464 | addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground", | 
|  | 1465 | "withObject", NULL); | 
| Ted Kremenek | 0806f91 | 2008-05-06 00:30:21 +0000 | [diff] [blame] | 1466 | } | 
|  | 1467 |  | 
| Ted Kremenek | ea736c5 | 2008-06-23 22:21:20 +0000 | [diff] [blame] | 1468 | void RetainSummaryManager::InitializeMethodSummaries() { | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1469 |  | 
|  | 1470 | assert (ScratchArgs.isEmpty()); | 
|  | 1471 |  | 
| Ted Kremenek | 767d074 | 2008-05-06 21:26:51 +0000 | [diff] [blame] | 1472 | // Create the "init" selector.  It just acts as a pass-through for the | 
|  | 1473 | // receiver. | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1474 | const RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg); | 
| Ted Kremenek | 815fbb6 | 2009-08-20 05:13:36 +0000 | [diff] [blame] | 1475 | addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm); | 
|  | 1476 |  | 
|  | 1477 | // awakeAfterUsingCoder: behaves basically like an 'init' method.  It | 
|  | 1478 | // claims the receiver and returns a retained object. | 
|  | 1479 | addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx), | 
|  | 1480 | InitSumm); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1481 |  | 
| Ted Kremenek | 767d074 | 2008-05-06 21:26:51 +0000 | [diff] [blame] | 1482 | // The next methods are allocators. | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1483 | const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE); | 
|  | 1484 | const RetainSummary *CFAllocSumm = | 
| Ted Kremenek | 52ac2b5 | 2009-08-28 19:52:12 +0000 | [diff] [blame] | 1485 | getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1486 |  | 
| Ted Kremenek | b0862dc | 2008-05-06 02:26:56 +0000 | [diff] [blame] | 1487 | // Create the "retain" selector. | 
| Jordy Rose | 3f7f7568 | 2011-08-21 19:41:36 +0000 | [diff] [blame] | 1488 | RetEffect NoRet = RetEffect::MakeNoRet(); | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1489 | const RetainSummary *Summ = getPersistentSummary(NoRet, IncRefMsg); | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 1490 | addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1491 |  | 
| Ted Kremenek | b0862dc | 2008-05-06 02:26:56 +0000 | [diff] [blame] | 1492 | // Create the "release" selector. | 
| Jordy Rose | 3f7f7568 | 2011-08-21 19:41:36 +0000 | [diff] [blame] | 1493 | Summ = getPersistentSummary(NoRet, DecRefMsg); | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 1494 | addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1495 |  | 
| Ted Kremenek | bcdb468 | 2008-05-07 21:17:39 +0000 | [diff] [blame] | 1496 | // Create the "drain" selector. | 
| Jordy Rose | 3f7f7568 | 2011-08-21 19:41:36 +0000 | [diff] [blame] | 1497 | Summ = getPersistentSummary(NoRet, isGCEnabled() ? DoNothing : DecRef); | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 1498 | addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1499 |  | 
| Ted Kremenek | ea072e3 | 2009-03-17 19:42:23 +0000 | [diff] [blame] | 1500 | // Create the -dealloc summary. | 
| Jordy Rose | 3f7f7568 | 2011-08-21 19:41:36 +0000 | [diff] [blame] | 1501 | Summ = getPersistentSummary(NoRet, Dealloc); | 
| Ted Kremenek | ea072e3 | 2009-03-17 19:42:23 +0000 | [diff] [blame] | 1502 | addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); | 
| Ted Kremenek | b0862dc | 2008-05-06 02:26:56 +0000 | [diff] [blame] | 1503 |  | 
|  | 1504 | // Create the "autorelease" selector. | 
| Jordy Rose | 3f7f7568 | 2011-08-21 19:41:36 +0000 | [diff] [blame] | 1505 | Summ = getPersistentSummary(NoRet, Autorelease); | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 1506 | addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1507 |  | 
| Ted Kremenek | 50db3d0 | 2009-02-23 17:45:03 +0000 | [diff] [blame] | 1508 | // Specially handle NSAutoreleasePool. | 
| Ted Kremenek | dce7846 | 2009-02-25 02:54:57 +0000 | [diff] [blame] | 1509 | addInstMethSummary("NSAutoreleasePool", "init", | 
| Jordy Rose | 3f7f7568 | 2011-08-21 19:41:36 +0000 | [diff] [blame] | 1510 | getPersistentSummary(NoRet, NewAutoreleasePool)); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1511 |  | 
|  | 1512 | // For NSWindow, allocated objects are (initially) self-owned. | 
| Ted Kremenek | e73f282 | 2009-02-23 02:51:29 +0000 | [diff] [blame] | 1513 | // FIXME: For now we opt for false negatives with NSWindow, as these objects | 
|  | 1514 | //  self-own themselves.  However, they only do this once they are displayed. | 
|  | 1515 | //  Thus, we need to track an NSWindow's display status. | 
|  | 1516 | //  This is tracked in <rdar://problem/6062711>. | 
| Ted Kremenek | 00dfe30 | 2009-03-04 23:30:42 +0000 | [diff] [blame] | 1517 | //  See also http://llvm.org/bugs/show_bug.cgi?id=3714. | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 1518 | const RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(), | 
| Ted Kremenek | 1272f70 | 2009-05-12 20:06:54 +0000 | [diff] [blame] | 1519 | StopTracking, | 
|  | 1520 | StopTracking); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1521 |  | 
| Ted Kremenek | 751e7e3 | 2009-04-03 19:02:51 +0000 | [diff] [blame] | 1522 | addClassMethSummary("NSWindow", "alloc", NoTrackYet); | 
|  | 1523 |  | 
| Ted Kremenek | 00dfe30 | 2009-03-04 23:30:42 +0000 | [diff] [blame] | 1524 | #if 0 | 
| Ted Kremenek | 1272f70 | 2009-05-12 20:06:54 +0000 | [diff] [blame] | 1525 | addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", | 
| Ted Kremenek | 3f13f59 | 2008-08-12 18:48:50 +0000 | [diff] [blame] | 1526 | "styleMask", "backing", "defer", NULL); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1527 |  | 
| Ted Kremenek | 1272f70 | 2009-05-12 20:06:54 +0000 | [diff] [blame] | 1528 | addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", | 
| Ted Kremenek | 3f13f59 | 2008-08-12 18:48:50 +0000 | [diff] [blame] | 1529 | "styleMask", "backing", "defer", "screen", NULL); | 
| Ted Kremenek | 00dfe30 | 2009-03-04 23:30:42 +0000 | [diff] [blame] | 1530 | #endif | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1531 |  | 
| Ted Kremenek | 3f13f59 | 2008-08-12 18:48:50 +0000 | [diff] [blame] | 1532 | // For NSPanel (which subclasses NSWindow), allocated objects are not | 
|  | 1533 | //  self-owned. | 
| Ted Kremenek | 751e7e3 | 2009-04-03 19:02:51 +0000 | [diff] [blame] | 1534 | // FIXME: For now we don't track NSPanels. object for the same reason | 
|  | 1535 | //   as for NSWindow objects. | 
|  | 1536 | addClassMethSummary("NSPanel", "alloc", NoTrackYet); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1537 |  | 
| Ted Kremenek | 1272f70 | 2009-05-12 20:06:54 +0000 | [diff] [blame] | 1538 | #if 0 | 
|  | 1539 | addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", | 
| Ted Kremenek | 3f13f59 | 2008-08-12 18:48:50 +0000 | [diff] [blame] | 1540 | "styleMask", "backing", "defer", NULL); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1541 |  | 
| Ted Kremenek | 1272f70 | 2009-05-12 20:06:54 +0000 | [diff] [blame] | 1542 | addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", | 
| Ted Kremenek | 3f13f59 | 2008-08-12 18:48:50 +0000 | [diff] [blame] | 1543 | "styleMask", "backing", "defer", "screen", NULL); | 
| Ted Kremenek | 1272f70 | 2009-05-12 20:06:54 +0000 | [diff] [blame] | 1544 | #endif | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1545 |  | 
| Ted Kremenek | 501ba03 | 2009-05-18 23:14:34 +0000 | [diff] [blame] | 1546 | // Don't track allocated autorelease pools yet, as it is okay to prematurely | 
|  | 1547 | // exit a method. | 
|  | 1548 | addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); | 
| Ted Kremenek | e8a5ba8 | 2012-02-18 21:37:48 +0000 | [diff] [blame] | 1549 | addClassMethSummary("NSAutoreleasePool", "allocWithZone", NoTrackYet, false); | 
| Ted Kremenek | 3185c9c | 2008-06-25 21:21:56 +0000 | [diff] [blame] | 1550 |  | 
| Ted Kremenek | 1036912 | 2009-05-20 22:39:57 +0000 | [diff] [blame] | 1551 | // Create summaries QCRenderer/QCView -createSnapShotImageOfType: | 
|  | 1552 | addInstMethSummary("QCRenderer", AllocSumm, | 
|  | 1553 | "createSnapshotImageOfType", NULL); | 
|  | 1554 | addInstMethSummary("QCView", AllocSumm, | 
|  | 1555 | "createSnapshotImageOfType", NULL); | 
|  | 1556 |  | 
| Ted Kremenek | 96aa146 | 2009-06-15 20:58:58 +0000 | [diff] [blame] | 1557 | // Create summaries for CIContext, 'createCGImage' and | 
| Ted Kremenek | 52ac2b5 | 2009-08-28 19:52:12 +0000 | [diff] [blame] | 1558 | // 'createCGLayerWithSize'.  These objects are CF objects, and are not | 
|  | 1559 | // automatically garbage collected. | 
|  | 1560 | addInstMethSummary("CIContext", CFAllocSumm, | 
| Ted Kremenek | 1036912 | 2009-05-20 22:39:57 +0000 | [diff] [blame] | 1561 | "createCGImage", "fromRect", NULL); | 
| Ted Kremenek | 52ac2b5 | 2009-08-28 19:52:12 +0000 | [diff] [blame] | 1562 | addInstMethSummary("CIContext", CFAllocSumm, | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1563 | "createCGImage", "fromRect", "format", "colorSpace", NULL); | 
| Ted Kremenek | 52ac2b5 | 2009-08-28 19:52:12 +0000 | [diff] [blame] | 1564 | addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", | 
| Ted Kremenek | 96aa146 | 2009-06-15 20:58:58 +0000 | [diff] [blame] | 1565 | "info", NULL); | 
| Ted Kremenek | be7c56e | 2008-05-06 00:38:54 +0000 | [diff] [blame] | 1566 | } | 
|  | 1567 |  | 
| Ted Kremenek | 00daccd | 2008-05-05 22:11:16 +0000 | [diff] [blame] | 1568 | //===----------------------------------------------------------------------===// | 
| Ted Kremenek | c52f939 | 2009-02-24 19:15:11 +0000 | [diff] [blame] | 1569 | // AutoreleaseBindings - State used to track objects in autorelease pools. | 
| Ted Kremenek | 0747e7e | 2008-10-21 15:53:15 +0000 | [diff] [blame] | 1570 | //===----------------------------------------------------------------------===// | 
|  | 1571 |  | 
| Ted Kremenek | c52f939 | 2009-02-24 19:15:11 +0000 | [diff] [blame] | 1572 | typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts; | 
|  | 1573 | typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents; | 
|  | 1574 | typedef llvm::ImmutableList<SymbolRef> ARStack; | 
| Ted Kremenek | 50db3d0 | 2009-02-23 17:45:03 +0000 | [diff] [blame] | 1575 |  | 
| Ted Kremenek | c52f939 | 2009-02-24 19:15:11 +0000 | [diff] [blame] | 1576 | static int AutoRCIndex = 0; | 
| Ted Kremenek | 0747e7e | 2008-10-21 15:53:15 +0000 | [diff] [blame] | 1577 | static int AutoRBIndex = 0; | 
|  | 1578 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1579 | namespace { class AutoreleasePoolContents {}; } | 
|  | 1580 | namespace { class AutoreleaseStack {}; } | 
| Ted Kremenek | c52f939 | 2009-02-24 19:15:11 +0000 | [diff] [blame] | 1581 |  | 
| Ted Kremenek | 0747e7e | 2008-10-21 15:53:15 +0000 | [diff] [blame] | 1582 | namespace clang { | 
| Ted Kremenek | 98857c9 | 2010-12-23 07:20:52 +0000 | [diff] [blame] | 1583 | namespace ento { | 
| Ted Kremenek | 001fd5b | 2011-08-15 22:09:50 +0000 | [diff] [blame] | 1584 | template<> struct ProgramStateTrait<AutoreleaseStack> | 
|  | 1585 | : public ProgramStatePartialTrait<ARStack> { | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 1586 | static inline void *GDMIndex() { return &AutoRBIndex; } | 
| Ted Kremenek | c52f939 | 2009-02-24 19:15:11 +0000 | [diff] [blame] | 1587 | }; | 
|  | 1588 |  | 
| Ted Kremenek | 001fd5b | 2011-08-15 22:09:50 +0000 | [diff] [blame] | 1589 | template<> struct ProgramStateTrait<AutoreleasePoolContents> | 
|  | 1590 | : public ProgramStatePartialTrait<ARPoolContents> { | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 1591 | static inline void *GDMIndex() { return &AutoRCIndex; } | 
| Ted Kremenek | c52f939 | 2009-02-24 19:15:11 +0000 | [diff] [blame] | 1592 | }; | 
| Argyrios Kyrtzidis | ca08fba | 2010-12-22 18:53:20 +0000 | [diff] [blame] | 1593 | } // end GR namespace | 
| Ted Kremenek | c52f939 | 2009-02-24 19:15:11 +0000 | [diff] [blame] | 1594 | } // end clang namespace | 
| Ted Kremenek | 0747e7e | 2008-10-21 15:53:15 +0000 | [diff] [blame] | 1595 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 1596 | static SymbolRef GetCurrentAutoreleasePool(ProgramStateRef state) { | 
| Ted Kremenek | 8c3f004 | 2009-03-20 17:34:15 +0000 | [diff] [blame] | 1597 | ARStack stack = state->get<AutoreleaseStack>(); | 
|  | 1598 | return stack.isEmpty() ? SymbolRef() : stack.getHead(); | 
|  | 1599 | } | 
|  | 1600 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 1601 | static ProgramStateRef | 
|  | 1602 | SendAutorelease(ProgramStateRef state, | 
| Ted Kremenek | 001fd5b | 2011-08-15 22:09:50 +0000 | [diff] [blame] | 1603 | ARCounts::Factory &F, | 
|  | 1604 | SymbolRef sym) { | 
| Ted Kremenek | 8c3f004 | 2009-03-20 17:34:15 +0000 | [diff] [blame] | 1605 | SymbolRef pool = GetCurrentAutoreleasePool(state); | 
| Ted Kremenek | d93c6e3 | 2009-06-18 01:23:53 +0000 | [diff] [blame] | 1606 | const ARCounts *cnts = state->get<AutoreleasePoolContents>(pool); | 
| Ted Kremenek | 8c3f004 | 2009-03-20 17:34:15 +0000 | [diff] [blame] | 1607 | ARCounts newCnts(0); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1608 |  | 
| Ted Kremenek | 8c3f004 | 2009-03-20 17:34:15 +0000 | [diff] [blame] | 1609 | if (cnts) { | 
|  | 1610 | const unsigned *cnt = (*cnts).lookup(sym); | 
| Ted Kremenek | b3b56c6 | 2010-11-24 00:54:37 +0000 | [diff] [blame] | 1611 | newCnts = F.add(*cnts, sym, cnt ? *cnt  + 1 : 1); | 
| Ted Kremenek | 8c3f004 | 2009-03-20 17:34:15 +0000 | [diff] [blame] | 1612 | } | 
|  | 1613 | else | 
| Ted Kremenek | b3b56c6 | 2010-11-24 00:54:37 +0000 | [diff] [blame] | 1614 | newCnts = F.add(F.getEmptyMap(), sym, 1); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1615 |  | 
| Ted Kremenek | d93c6e3 | 2009-06-18 01:23:53 +0000 | [diff] [blame] | 1616 | return state->set<AutoreleasePoolContents>(pool, newCnts); | 
| Ted Kremenek | 8c3f004 | 2009-03-20 17:34:15 +0000 | [diff] [blame] | 1617 | } | 
|  | 1618 |  | 
| Ted Kremenek | 7145489 | 2008-04-16 20:40:59 +0000 | [diff] [blame] | 1619 | //===----------------------------------------------------------------------===// | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1620 | // Error reporting. | 
|  | 1621 | //===----------------------------------------------------------------------===// | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1622 | namespace { | 
| Jordy Rose | 20d4e68 | 2011-08-23 20:55:48 +0000 | [diff] [blame] | 1623 | typedef llvm::DenseMap<const ExplodedNode *, const RetainSummary *> | 
|  | 1624 | SummaryLogTy; | 
|  | 1625 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1626 | //===-------------===// | 
|  | 1627 | // Bug Descriptions. // | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1628 | //===-------------===// | 
|  | 1629 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1630 | class CFRefBug : public BugType { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1631 | protected: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1632 | CFRefBug(StringRef name) | 
| Ted Kremenek | b45d198 | 2012-04-05 20:43:28 +0000 | [diff] [blame] | 1633 | : BugType(name, categories::MemoryCoreFoundationObjectiveC) {} | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1634 | public: | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1635 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1636 | // FIXME: Eventually remove. | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1637 | virtual const char *getDescription() const = 0; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1638 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1639 | virtual bool isLeak() const { return false; } | 
|  | 1640 | }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1641 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1642 | class UseAfterRelease : public CFRefBug { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1643 | public: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1644 | UseAfterRelease() : CFRefBug("Use-after-release") {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1645 |  | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1646 | const char *getDescription() const { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1647 | return "Reference-counted object is used after it is released"; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1648 | } | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1649 | }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1650 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1651 | class BadRelease : public CFRefBug { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1652 | public: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1653 | BadRelease() : CFRefBug("Bad release") {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1654 |  | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1655 | const char *getDescription() const { | 
| Ted Kremenek | 5c22e11 | 2009-10-01 17:31:50 +0000 | [diff] [blame] | 1656 | return "Incorrect decrement of the reference count of an object that is " | 
|  | 1657 | "not owned at this point by the caller"; | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1658 | } | 
|  | 1659 | }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1660 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1661 | class DeallocGC : public CFRefBug { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1662 | public: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1663 | DeallocGC() | 
|  | 1664 | : CFRefBug("-dealloc called while using garbage collection") {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1665 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1666 | const char *getDescription() const { | 
| Ted Kremenek | d35272f | 2009-05-09 00:10:05 +0000 | [diff] [blame] | 1667 | return "-dealloc called while using garbage collection"; | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1668 | } | 
|  | 1669 | }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1670 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1671 | class DeallocNotOwned : public CFRefBug { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1672 | public: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1673 | DeallocNotOwned() | 
|  | 1674 | : CFRefBug("-dealloc sent to non-exclusively owned object") {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1675 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1676 | const char *getDescription() const { | 
|  | 1677 | return "-dealloc sent to object that may be referenced elsewhere"; | 
|  | 1678 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1679 | }; | 
|  | 1680 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1681 | class OverAutorelease : public CFRefBug { | 
| Ted Kremenek | d35272f | 2009-05-09 00:10:05 +0000 | [diff] [blame] | 1682 | public: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1683 | OverAutorelease() | 
|  | 1684 | : CFRefBug("Object sent -autorelease too many times") {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1685 |  | 
| Ted Kremenek | d35272f | 2009-05-09 00:10:05 +0000 | [diff] [blame] | 1686 | const char *getDescription() const { | 
| Ted Kremenek | 3978f79 | 2009-05-10 05:11:21 +0000 | [diff] [blame] | 1687 | return "Object sent -autorelease too many times"; | 
| Ted Kremenek | d35272f | 2009-05-09 00:10:05 +0000 | [diff] [blame] | 1688 | } | 
|  | 1689 | }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1690 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1691 | class ReturnedNotOwnedForOwned : public CFRefBug { | 
| Ted Kremenek | dee56e3 | 2009-05-10 06:25:57 +0000 | [diff] [blame] | 1692 | public: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1693 | ReturnedNotOwnedForOwned() | 
|  | 1694 | : CFRefBug("Method should return an owned object") {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1695 |  | 
| Ted Kremenek | dee56e3 | 2009-05-10 06:25:57 +0000 | [diff] [blame] | 1696 | const char *getDescription() const { | 
| Jordy Rose | 43426f8 | 2011-07-15 22:17:54 +0000 | [diff] [blame] | 1697 | return "Object with a +0 retain count returned to caller where a +1 " | 
| Ted Kremenek | dee56e3 | 2009-05-10 06:25:57 +0000 | [diff] [blame] | 1698 | "(owning) retain count is expected"; | 
|  | 1699 | } | 
|  | 1700 | }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1701 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1702 | class Leak : public CFRefBug { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1703 | const bool isReturn; | 
|  | 1704 | protected: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1705 | Leak(StringRef name, bool isRet) | 
| Jordy Rose | 15484da | 2011-08-25 01:14:38 +0000 | [diff] [blame] | 1706 | : CFRefBug(name), isReturn(isRet) { | 
|  | 1707 | // Leaks should not be reported if they are post-dominated by a sink. | 
|  | 1708 | setSuppressOnSink(true); | 
|  | 1709 | } | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1710 | public: | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1711 |  | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1712 | const char *getDescription() const { return ""; } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1713 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1714 | bool isLeak() const { return true; } | 
|  | 1715 | }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1716 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1717 | class LeakAtReturn : public Leak { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1718 | public: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1719 | LeakAtReturn(StringRef name) | 
|  | 1720 | : Leak(name, true) {} | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1721 | }; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1722 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1723 | class LeakWithinFunction : public Leak { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1724 | public: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1725 | LeakWithinFunction(StringRef name) | 
|  | 1726 | : Leak(name, false) {} | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1727 | }; | 
|  | 1728 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1729 | //===---------===// | 
|  | 1730 | // Bug Reports.  // | 
|  | 1731 | //===---------===// | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1732 |  | 
| Jordy Rose | f78877e | 2012-03-24 02:45:35 +0000 | [diff] [blame] | 1733 | class CFRefReportVisitor : public BugReporterVisitorImpl<CFRefReportVisitor> { | 
| Anna Zaks | 88255cc | 2011-08-20 01:27:22 +0000 | [diff] [blame] | 1734 | protected: | 
| Anna Zaks | 071a89c | 2011-08-19 23:21:56 +0000 | [diff] [blame] | 1735 | SymbolRef Sym; | 
| Jordy Rose | 20d4e68 | 2011-08-23 20:55:48 +0000 | [diff] [blame] | 1736 | const SummaryLogTy &SummaryLog; | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1737 | bool GCEnabled; | 
| Anna Zaks | 88255cc | 2011-08-20 01:27:22 +0000 | [diff] [blame] | 1738 |  | 
| Anna Zaks | 071a89c | 2011-08-19 23:21:56 +0000 | [diff] [blame] | 1739 | public: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1740 | CFRefReportVisitor(SymbolRef sym, bool gcEnabled, const SummaryLogTy &log) | 
|  | 1741 | : Sym(sym), SummaryLog(log), GCEnabled(gcEnabled) {} | 
| Anna Zaks | 071a89c | 2011-08-19 23:21:56 +0000 | [diff] [blame] | 1742 |  | 
| Anna Zaks | 88255cc | 2011-08-20 01:27:22 +0000 | [diff] [blame] | 1743 | virtual void Profile(llvm::FoldingSetNodeID &ID) const { | 
| Anna Zaks | 071a89c | 2011-08-19 23:21:56 +0000 | [diff] [blame] | 1744 | static int x = 0; | 
|  | 1745 | ID.AddPointer(&x); | 
|  | 1746 | ID.AddPointer(Sym); | 
|  | 1747 | } | 
|  | 1748 |  | 
| Anna Zaks | 88255cc | 2011-08-20 01:27:22 +0000 | [diff] [blame] | 1749 | virtual PathDiagnosticPiece *VisitNode(const ExplodedNode *N, | 
|  | 1750 | const ExplodedNode *PrevN, | 
|  | 1751 | BugReporterContext &BRC, | 
|  | 1752 | BugReport &BR); | 
|  | 1753 |  | 
|  | 1754 | virtual PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, | 
|  | 1755 | const ExplodedNode *N, | 
|  | 1756 | BugReport &BR); | 
|  | 1757 | }; | 
|  | 1758 |  | 
|  | 1759 | class CFRefLeakReportVisitor : public CFRefReportVisitor { | 
|  | 1760 | public: | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1761 | CFRefLeakReportVisitor(SymbolRef sym, bool GCEnabled, | 
| Jordy Rose | 20d4e68 | 2011-08-23 20:55:48 +0000 | [diff] [blame] | 1762 | const SummaryLogTy &log) | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1763 | : CFRefReportVisitor(sym, GCEnabled, log) {} | 
| Anna Zaks | 88255cc | 2011-08-20 01:27:22 +0000 | [diff] [blame] | 1764 |  | 
|  | 1765 | PathDiagnosticPiece *getEndPath(BugReporterContext &BRC, | 
|  | 1766 | const ExplodedNode *N, | 
|  | 1767 | BugReport &BR); | 
| Jordy Rose | f78877e | 2012-03-24 02:45:35 +0000 | [diff] [blame] | 1768 |  | 
|  | 1769 | virtual BugReporterVisitor *clone() const { | 
|  | 1770 | // The curiously-recurring template pattern only works for one level of | 
|  | 1771 | // subclassing. Rather than make a new template base for | 
|  | 1772 | // CFRefReportVisitor, we simply override clone() to do the right thing. | 
|  | 1773 | // This could be trouble someday if BugReporterVisitorImpl is ever | 
|  | 1774 | // used for something else besides a convenient implementation of clone(). | 
|  | 1775 | return new CFRefLeakReportVisitor(*this); | 
|  | 1776 | } | 
| Anna Zaks | 071a89c | 2011-08-19 23:21:56 +0000 | [diff] [blame] | 1777 | }; | 
|  | 1778 |  | 
| Anna Zaks | 3a6bdf8 | 2011-08-17 23:00:25 +0000 | [diff] [blame] | 1779 | class CFRefReport : public BugReport { | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 1780 | void addGCModeDescription(const LangOptions &LOpts, bool GCEnabled); | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1781 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1782 | public: | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 1783 | CFRefReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, | 
|  | 1784 | const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, | 
|  | 1785 | bool registerVisitor = true) | 
| Anna Zaks | 752de14 | 2011-08-22 18:54:07 +0000 | [diff] [blame] | 1786 | : BugReport(D, D.getDescription(), n) { | 
| Anna Zaks | 88255cc | 2011-08-20 01:27:22 +0000 | [diff] [blame] | 1787 | if (registerVisitor) | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 1788 | addVisitor(new CFRefReportVisitor(sym, GCEnabled, Log)); | 
|  | 1789 | addGCModeDescription(LOpts, GCEnabled); | 
| Anna Zaks | 071a89c | 2011-08-19 23:21:56 +0000 | [diff] [blame] | 1790 | } | 
| Ted Kremenek | 3978f79 | 2009-05-10 05:11:21 +0000 | [diff] [blame] | 1791 |  | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 1792 | CFRefReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, | 
|  | 1793 | const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, | 
|  | 1794 | StringRef endText) | 
| Anna Zaks | 752de14 | 2011-08-22 18:54:07 +0000 | [diff] [blame] | 1795 | : BugReport(D, D.getDescription(), endText, n) { | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 1796 | addVisitor(new CFRefReportVisitor(sym, GCEnabled, Log)); | 
|  | 1797 | addGCModeDescription(LOpts, GCEnabled); | 
| Anna Zaks | 071a89c | 2011-08-19 23:21:56 +0000 | [diff] [blame] | 1798 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1799 |  | 
| Anna Zaks | 3a6bdf8 | 2011-08-17 23:00:25 +0000 | [diff] [blame] | 1800 | virtual std::pair<ranges_iterator, ranges_iterator> getRanges() { | 
| Anna Zaks | 752de14 | 2011-08-22 18:54:07 +0000 | [diff] [blame] | 1801 | const CFRefBug& BugTy = static_cast<CFRefBug&>(getBugType()); | 
|  | 1802 | if (!BugTy.isLeak()) | 
| Anna Zaks | 3a6bdf8 | 2011-08-17 23:00:25 +0000 | [diff] [blame] | 1803 | return BugReport::getRanges(); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1804 | else | 
| Argyrios Kyrtzidis | d22d8ff | 2010-12-04 01:12:15 +0000 | [diff] [blame] | 1805 | return std::make_pair(ranges_iterator(), ranges_iterator()); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1806 | } | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1807 | }; | 
| Ted Kremenek | 3978f79 | 2009-05-10 05:11:21 +0000 | [diff] [blame] | 1808 |  | 
| Kovarththanan Rajaratnam | 65c6566 | 2009-11-28 06:07:30 +0000 | [diff] [blame] | 1809 | class CFRefLeakReport : public CFRefReport { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1810 | const MemRegion* AllocBinding; | 
| Anna Zaks | 88255cc | 2011-08-20 01:27:22 +0000 | [diff] [blame] | 1811 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1812 | public: | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 1813 | CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, bool GCEnabled, | 
|  | 1814 | const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 1815 | CheckerContext &Ctx); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1816 |  | 
| Anna Zaks | c29bed3 | 2011-09-20 21:38:35 +0000 | [diff] [blame] | 1817 | PathDiagnosticLocation getLocation(const SourceManager &SM) const { | 
|  | 1818 | assert(Location.isValid()); | 
|  | 1819 | return Location; | 
|  | 1820 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1821 | }; | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1822 | } // end anonymous namespace | 
|  | 1823 |  | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 1824 | void CFRefReport::addGCModeDescription(const LangOptions &LOpts, | 
|  | 1825 | bool GCEnabled) { | 
| Jordy Rose | 9ff0299 | 2011-08-24 20:38:42 +0000 | [diff] [blame] | 1826 | const char *GCModeDescription = 0; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1827 |  | 
| Douglas Gregor | 79a9141 | 2011-09-13 17:21:33 +0000 | [diff] [blame] | 1828 | switch (LOpts.getGC()) { | 
| Anna Zaks | 76c3fb6 | 2011-08-22 20:31:28 +0000 | [diff] [blame] | 1829 | case LangOptions::GCOnly: | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 1830 | assert(GCEnabled); | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1831 | GCModeDescription = "Code is compiled to only use garbage collection"; | 
|  | 1832 | break; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1833 |  | 
| Anna Zaks | 76c3fb6 | 2011-08-22 20:31:28 +0000 | [diff] [blame] | 1834 | case LangOptions::NonGC: | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 1835 | assert(!GCEnabled); | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1836 | GCModeDescription = "Code is compiled to use reference counts"; | 
|  | 1837 | break; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1838 |  | 
| Anna Zaks | 76c3fb6 | 2011-08-22 20:31:28 +0000 | [diff] [blame] | 1839 | case LangOptions::HybridGC: | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 1840 | if (GCEnabled) { | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1841 | GCModeDescription = "Code is compiled to use either garbage collection " | 
|  | 1842 | "(GC) or reference counts (non-GC).  The bug occurs " | 
|  | 1843 | "with GC enabled"; | 
|  | 1844 | break; | 
|  | 1845 | } else { | 
|  | 1846 | GCModeDescription = "Code is compiled to use either garbage collection " | 
|  | 1847 | "(GC) or reference counts (non-GC).  The bug occurs " | 
|  | 1848 | "in non-GC mode"; | 
|  | 1849 | break; | 
| Anna Zaks | 76c3fb6 | 2011-08-22 20:31:28 +0000 | [diff] [blame] | 1850 | } | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1851 | } | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1852 |  | 
| Jordy Rose | 9ff0299 | 2011-08-24 20:38:42 +0000 | [diff] [blame] | 1853 | assert(GCModeDescription && "invalid/unknown GC mode"); | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 1854 | addExtraText(GCModeDescription); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1855 | } | 
|  | 1856 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 1857 | // FIXME: This should be a method on SmallVector. | 
| Chris Lattner | 0e62c1c | 2011-07-23 10:55:15 +0000 | [diff] [blame] | 1858 | static inline bool contains(const SmallVectorImpl<ArgEffect>& V, | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1859 | ArgEffect X) { | 
| Chris Lattner | 0e62c1c | 2011-07-23 10:55:15 +0000 | [diff] [blame] | 1860 | for (SmallVectorImpl<ArgEffect>::const_iterator I=V.begin(), E=V.end(); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1861 | I!=E; ++I) | 
|  | 1862 | if (*I == X) return true; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1863 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1864 | return false; | 
|  | 1865 | } | 
|  | 1866 |  | 
| Jordy Rose | 6393f82 | 2012-05-12 05:10:43 +0000 | [diff] [blame] | 1867 | static bool isNumericLiteralExpression(const Expr *E) { | 
|  | 1868 | // FIXME: This set of cases was copied from SemaExprObjC. | 
|  | 1869 | return isa<IntegerLiteral>(E) || | 
|  | 1870 | isa<CharacterLiteral>(E) || | 
|  | 1871 | isa<FloatingLiteral>(E) || | 
|  | 1872 | isa<ObjCBoolLiteralExpr>(E) || | 
|  | 1873 | isa<CXXBoolLiteralExpr>(E); | 
|  | 1874 | } | 
|  | 1875 |  | 
| Ted Kremenek | 8e7fbcc | 2011-11-14 21:59:21 +0000 | [diff] [blame] | 1876 | static bool isPropertyAccess(const Stmt *S, ParentMap &PM) { | 
|  | 1877 | unsigned maxDepth = 4; | 
|  | 1878 | while (S && maxDepth) { | 
|  | 1879 | if (const PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(S)) { | 
|  | 1880 | if (!isa<ObjCMessageExpr>(PO->getSyntacticForm())) | 
|  | 1881 | return true; | 
|  | 1882 | return false; | 
|  | 1883 | } | 
|  | 1884 | S = PM.getParent(S); | 
|  | 1885 | --maxDepth; | 
|  | 1886 | } | 
|  | 1887 | return false; | 
|  | 1888 | } | 
|  | 1889 |  | 
| Anna Zaks | 071a89c | 2011-08-19 23:21:56 +0000 | [diff] [blame] | 1890 | PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, | 
|  | 1891 | const ExplodedNode *PrevN, | 
|  | 1892 | BugReporterContext &BRC, | 
|  | 1893 | BugReport &BR) { | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1894 |  | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 1895 | if (!isa<StmtPoint>(N->getLocation())) | 
| Ted Kremenek | 051a03d | 2009-05-13 07:12:33 +0000 | [diff] [blame] | 1896 | return NULL; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1897 |  | 
| Ted Kremenek | bb8d546 | 2009-05-06 21:39:49 +0000 | [diff] [blame] | 1898 | // Check if the type state has changed. | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 1899 | ProgramStateRef PrevSt = PrevN->getState(); | 
|  | 1900 | ProgramStateRef CurrSt = N->getState(); | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 1901 | const LocationContext *LCtx = N->getLocationContext(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1902 |  | 
|  | 1903 | const RefVal* CurrT = CurrSt->get<RefBindings>(Sym); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1904 | if (!CurrT) return NULL; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1905 |  | 
| Ted Kremenek | d93c6e3 | 2009-06-18 01:23:53 +0000 | [diff] [blame] | 1906 | const RefVal &CurrV = *CurrT; | 
|  | 1907 | const RefVal *PrevT = PrevSt->get<RefBindings>(Sym); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1908 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1909 | // Create a string buffer to constain all the useful things we want | 
|  | 1910 | // to tell the user. | 
|  | 1911 | std::string sbuf; | 
|  | 1912 | llvm::raw_string_ostream os(sbuf); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1913 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1914 | // This is the allocation site since the previous node had no bindings | 
|  | 1915 | // for this symbol. | 
|  | 1916 | if (!PrevT) { | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 1917 | const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1918 |  | 
| Ted Kremenek | 415287d | 2012-03-06 20:06:12 +0000 | [diff] [blame] | 1919 | if (isa<ObjCArrayLiteral>(S)) { | 
|  | 1920 | os << "NSArray literal is an object with a +0 retain count"; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1921 | } | 
| Ted Kremenek | 415287d | 2012-03-06 20:06:12 +0000 | [diff] [blame] | 1922 | else if (isa<ObjCDictionaryLiteral>(S)) { | 
|  | 1923 | os << "NSDictionary literal is an object with a +0 retain count"; | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1924 | } | 
| Jordy Rose | 6393f82 | 2012-05-12 05:10:43 +0000 | [diff] [blame] | 1925 | else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { | 
|  | 1926 | if (isNumericLiteralExpression(BL->getSubExpr())) | 
|  | 1927 | os << "NSNumber literal is an object with a +0 retain count"; | 
|  | 1928 | else { | 
|  | 1929 | const ObjCInterfaceDecl *BoxClass = 0; | 
|  | 1930 | if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) | 
|  | 1931 | BoxClass = Method->getClassInterface(); | 
|  | 1932 |  | 
|  | 1933 | // We should always be able to find the boxing class interface, | 
|  | 1934 | // but consider this future-proofing. | 
|  | 1935 | if (BoxClass) | 
|  | 1936 | os << *BoxClass << " b"; | 
|  | 1937 | else | 
|  | 1938 | os << "B"; | 
|  | 1939 |  | 
|  | 1940 | os << "oxed expression produces an object with a +0 retain count"; | 
|  | 1941 | } | 
|  | 1942 | } | 
| Ted Kremenek | 415287d | 2012-03-06 20:06:12 +0000 | [diff] [blame] | 1943 | else { | 
|  | 1944 | if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { | 
|  | 1945 | // Get the name of the callee (if it is available). | 
|  | 1946 | SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); | 
|  | 1947 | if (const FunctionDecl *FD = X.getAsFunctionDecl()) | 
|  | 1948 | os << "Call to function '" << *FD << '\''; | 
|  | 1949 | else | 
|  | 1950 | os << "function call"; | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1951 | } | 
| Ted Kremenek | 415287d | 2012-03-06 20:06:12 +0000 | [diff] [blame] | 1952 | else { | 
|  | 1953 | assert(isa<ObjCMessageExpr>(S)); | 
|  | 1954 | // The message expression may have between written directly or as | 
|  | 1955 | // a property access.  Lazily determine which case we are looking at. | 
|  | 1956 | os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method"); | 
|  | 1957 | } | 
|  | 1958 |  | 
|  | 1959 | if (CurrV.getObjKind() == RetEffect::CF) { | 
|  | 1960 | os << " returns a Core Foundation object with a "; | 
|  | 1961 | } | 
|  | 1962 | else { | 
|  | 1963 | assert (CurrV.getObjKind() == RetEffect::ObjC); | 
|  | 1964 | os << " returns an Objective-C object with a "; | 
|  | 1965 | } | 
|  | 1966 |  | 
|  | 1967 | if (CurrV.isOwned()) { | 
|  | 1968 | os << "+1 retain count"; | 
|  | 1969 |  | 
|  | 1970 | if (GCEnabled) { | 
|  | 1971 | assert(CurrV.getObjKind() == RetEffect::CF); | 
|  | 1972 | os << ".  " | 
|  | 1973 | "Core Foundation objects are not automatically garbage collected."; | 
|  | 1974 | } | 
|  | 1975 | } | 
|  | 1976 | else { | 
|  | 1977 | assert (CurrV.isNotOwned()); | 
|  | 1978 | os << "+0 retain count"; | 
|  | 1979 | } | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1980 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1981 |  | 
| Anna Zaks | 3a769bd | 2011-09-15 01:08:34 +0000 | [diff] [blame] | 1982 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), | 
|  | 1983 | N->getLocationContext()); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1984 | return new PathDiagnosticEventPiece(Pos, os.str()); | 
|  | 1985 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1986 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1987 | // Gather up the effects that were performed on the object at this | 
|  | 1988 | // program point | 
| Chris Lattner | 0e62c1c | 2011-07-23 10:55:15 +0000 | [diff] [blame] | 1989 | SmallVector<ArgEffect, 2> AEffects; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1990 |  | 
| Jordy Rose | 20d4e68 | 2011-08-23 20:55:48 +0000 | [diff] [blame] | 1991 | const ExplodedNode *OrigNode = BRC.getNodeResolver().getOriginalNode(N); | 
|  | 1992 | if (const RetainSummary *Summ = SummaryLog.lookup(OrigNode)) { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1993 | // We only have summaries attached to nodes after evaluating CallExpr and | 
|  | 1994 | // ObjCMessageExprs. | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 1995 | const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 1996 |  | 
| Ted Kremenek | bfd28fd | 2009-07-22 22:35:28 +0000 | [diff] [blame] | 1997 | if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 1998 | // Iterate through the parameter expressions and see if the symbol | 
|  | 1999 | // was ever passed as an argument. | 
|  | 2000 | unsigned i = 0; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2001 |  | 
| Ted Kremenek | bfd28fd | 2009-07-22 22:35:28 +0000 | [diff] [blame] | 2002 | for (CallExpr::const_arg_iterator AI=CE->arg_begin(), AE=CE->arg_end(); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2003 | AI!=AE; ++AI, ++i) { | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2004 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2005 | // Retrieve the value of the argument.  Is it the symbol | 
|  | 2006 | // we are interested in? | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2007 | if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym) | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2008 | continue; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2009 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2010 | // We have an argument.  Get the effect! | 
|  | 2011 | AEffects.push_back(Summ->getArg(i)); | 
|  | 2012 | } | 
|  | 2013 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2014 | else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { | 
| Douglas Gregor | 9a12919 | 2010-04-21 00:45:42 +0000 | [diff] [blame] | 2015 | if (const Expr *receiver = ME->getInstanceReceiver()) | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2016 | if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) | 
|  | 2017 | .getAsLocSymbol() == Sym) { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2018 | // The symbol we are tracking is the receiver. | 
|  | 2019 | AEffects.push_back(Summ->getReceiverEffect()); | 
|  | 2020 | } | 
|  | 2021 | } | 
|  | 2022 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2023 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2024 | do { | 
|  | 2025 | // Get the previous type state. | 
|  | 2026 | RefVal PrevV = *PrevT; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2027 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2028 | // Specially handle -dealloc. | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 2029 | if (!GCEnabled && contains(AEffects, Dealloc)) { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2030 | // Determine if the object's reference count was pushed to zero. | 
|  | 2031 | assert(!(PrevV == CurrV) && "The typestate *must* have changed."); | 
|  | 2032 | // We may not have transitioned to 'release' if we hit an error. | 
|  | 2033 | // This case is handled elsewhere. | 
|  | 2034 | if (CurrV.getKind() == RefVal::Released) { | 
| Ted Kremenek | 3a0516b | 2009-05-08 20:01:42 +0000 | [diff] [blame] | 2035 | assert(CurrV.getCombinedCounts() == 0); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2036 | os << "Object released by directly sending the '-dealloc' message"; | 
|  | 2037 | break; | 
|  | 2038 | } | 
|  | 2039 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2040 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2041 | // Specially handle CFMakeCollectable and friends. | 
|  | 2042 | if (contains(AEffects, MakeCollectable)) { | 
|  | 2043 | // Get the name of the function. | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 2044 | const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2045 | SVal X = | 
|  | 2046 | CurrSt->getSValAsScalarOrLoc(cast<CallExpr>(S)->getCallee(), LCtx); | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 2047 | const FunctionDecl *FD = X.getAsFunctionDecl(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2048 |  | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 2049 | if (GCEnabled) { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2050 | // Determine if the object's reference count was pushed to zero. | 
|  | 2051 | assert(!(PrevV == CurrV) && "The typestate *must* have changed."); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2052 |  | 
| Benjamin Kramer | b89514a | 2011-10-14 18:45:37 +0000 | [diff] [blame] | 2053 | os << "In GC mode a call to '" << *FD | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2054 | <<  "' decrements an object's retain count and registers the " | 
|  | 2055 | "object with the garbage collector. "; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2056 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2057 | if (CurrV.getKind() == RefVal::Released) { | 
|  | 2058 | assert(CurrV.getCount() == 0); | 
|  | 2059 | os << "Since it now has a 0 retain count the object can be " | 
|  | 2060 | "automatically collected by the garbage collector."; | 
|  | 2061 | } | 
|  | 2062 | else | 
|  | 2063 | os << "An object must have a 0 retain count to be garbage collected. " | 
|  | 2064 | "After this call its retain count is +" << CurrV.getCount() | 
|  | 2065 | << '.'; | 
|  | 2066 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2067 | else | 
| Benjamin Kramer | b89514a | 2011-10-14 18:45:37 +0000 | [diff] [blame] | 2068 | os << "When GC is not enabled a call to '" << *FD | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2069 | << "' has no effect on its argument."; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2070 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2071 | // Nothing more to say. | 
|  | 2072 | break; | 
|  | 2073 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2074 |  | 
|  | 2075 | // Determine if the typestate has changed. | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2076 | if (!(PrevV == CurrV)) | 
|  | 2077 | switch (CurrV.getKind()) { | 
|  | 2078 | case RefVal::Owned: | 
|  | 2079 | case RefVal::NotOwned: | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2080 |  | 
| Ted Kremenek | 3a0516b | 2009-05-08 20:01:42 +0000 | [diff] [blame] | 2081 | if (PrevV.getCount() == CurrV.getCount()) { | 
|  | 2082 | // Did an autorelease message get sent? | 
|  | 2083 | if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount()) | 
|  | 2084 | return 0; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2085 |  | 
| Zhongxing Xu | 08a2ede | 2009-05-12 10:10:00 +0000 | [diff] [blame] | 2086 | assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); | 
| Ted Kremenek | 3978f79 | 2009-05-10 05:11:21 +0000 | [diff] [blame] | 2087 | os << "Object sent -autorelease message"; | 
| Ted Kremenek | 3a0516b | 2009-05-08 20:01:42 +0000 | [diff] [blame] | 2088 | break; | 
|  | 2089 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2090 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2091 | if (PrevV.getCount() > CurrV.getCount()) | 
|  | 2092 | os << "Reference count decremented."; | 
|  | 2093 | else | 
|  | 2094 | os << "Reference count incremented."; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2095 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2096 | if (unsigned Count = CurrV.getCount()) | 
|  | 2097 | os << " The object now has a +" << Count << " retain count."; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2098 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2099 | if (PrevV.getKind() == RefVal::Released) { | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 2100 | assert(GCEnabled && CurrV.getCount() > 0); | 
| Jordy Rose | 78373e5 | 2012-03-17 05:49:15 +0000 | [diff] [blame] | 2101 | os << " The object is not eligible for garbage collection until " | 
|  | 2102 | "the retain count reaches 0 again."; | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2103 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2104 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2105 | break; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2106 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2107 | case RefVal::Released: | 
|  | 2108 | os << "Object released."; | 
|  | 2109 | break; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2110 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2111 | case RefVal::ReturnedOwned: | 
| Jordy Rose | 78373e5 | 2012-03-17 05:49:15 +0000 | [diff] [blame] | 2112 | // Autoreleases can be applied after marking a node ReturnedOwned. | 
|  | 2113 | if (CurrV.getAutoreleaseCount()) | 
|  | 2114 | return NULL; | 
|  | 2115 |  | 
|  | 2116 | os << "Object returned to caller as an owning reference (single " | 
|  | 2117 | "retain count transferred to caller)"; | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2118 | break; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2119 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2120 | case RefVal::ReturnedNotOwned: | 
| Ted Kremenek | f230198 | 2011-05-26 18:45:44 +0000 | [diff] [blame] | 2121 | os << "Object returned to caller with a +0 retain count"; | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2122 | break; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2123 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2124 | default: | 
|  | 2125 | return NULL; | 
|  | 2126 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2127 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2128 | // Emit any remaining diagnostics for the argument effects (if any). | 
| Chris Lattner | 0e62c1c | 2011-07-23 10:55:15 +0000 | [diff] [blame] | 2129 | for (SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(), | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2130 | E=AEffects.end(); I != E; ++I) { | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2131 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2132 | // A bunch of things have alternate behavior under GC. | 
| Jordy Rose | 7a53498 | 2011-08-24 05:47:39 +0000 | [diff] [blame] | 2133 | if (GCEnabled) | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2134 | switch (*I) { | 
|  | 2135 | default: break; | 
|  | 2136 | case Autorelease: | 
|  | 2137 | os << "In GC mode an 'autorelease' has no effect."; | 
|  | 2138 | continue; | 
|  | 2139 | case IncRefMsg: | 
|  | 2140 | os << "In GC mode the 'retain' message has no effect."; | 
|  | 2141 | continue; | 
|  | 2142 | case DecRefMsg: | 
|  | 2143 | os << "In GC mode the 'release' message has no effect."; | 
|  | 2144 | continue; | 
|  | 2145 | } | 
|  | 2146 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2147 | } while (0); | 
|  | 2148 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2149 | if (os.str().empty()) | 
|  | 2150 | return 0; // We have nothing to say! | 
| Ted Kremenek | 051a03d | 2009-05-13 07:12:33 +0000 | [diff] [blame] | 2151 |  | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 2152 | const Stmt *S = cast<StmtPoint>(N->getLocation()).getStmt(); | 
| Anna Zaks | 3a769bd | 2011-09-15 01:08:34 +0000 | [diff] [blame] | 2153 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), | 
|  | 2154 | N->getLocationContext()); | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 2155 | PathDiagnosticPiece *P = new PathDiagnosticEventPiece(Pos, os.str()); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2156 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2157 | // Add the range by scanning the children of the statement for any bindings | 
|  | 2158 | // to Sym. | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2159 | for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); | 
| Ted Kremenek | bfd28fd | 2009-07-22 22:35:28 +0000 | [diff] [blame] | 2160 | I!=E; ++I) | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 2161 | if (const Expr *Exp = dyn_cast_or_null<Expr>(*I)) | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2162 | if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) { | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2163 | P->addRange(Exp->getSourceRange()); | 
|  | 2164 | break; | 
|  | 2165 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2166 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2167 | return P; | 
|  | 2168 | } | 
|  | 2169 |  | 
| Anna Zaks | 75de323 | 2012-02-28 22:39:22 +0000 | [diff] [blame] | 2170 | // Find the first node in the current function context that referred to the | 
|  | 2171 | // tracked symbol and the memory location that value was stored to. Note, the | 
|  | 2172 | // value is only reported if the allocation occurred in the same function as | 
|  | 2173 | // the leak. | 
| Zhongxing Xu | 20227f7 | 2009-08-06 01:32:16 +0000 | [diff] [blame] | 2174 | static std::pair<const ExplodedNode*,const MemRegion*> | 
| Ted Kremenek | 001fd5b | 2011-08-15 22:09:50 +0000 | [diff] [blame] | 2175 | GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N, | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2176 | SymbolRef Sym) { | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 2177 | const ExplodedNode *Last = N; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2178 | const MemRegion* FirstBinding = 0; | 
| Anna Zaks | 75de323 | 2012-02-28 22:39:22 +0000 | [diff] [blame] | 2179 | const LocationContext *LeakContext = N->getLocationContext(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2180 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2181 | while (N) { | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2182 | ProgramStateRef St = N->getState(); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2183 | RefBindings B = St->get<RefBindings>(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2184 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2185 | if (!B.lookup(Sym)) | 
|  | 2186 | break; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2187 |  | 
| Anna Zaks | 6797d6e | 2012-03-21 19:45:01 +0000 | [diff] [blame] | 2188 | StoreManager::FindUniqueBinding FB(Sym); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2189 | StateMgr.iterBindings(St, FB); | 
|  | 2190 | if (FB) FirstBinding = FB.getRegion(); | 
|  | 2191 |  | 
| Anna Zaks | 75de323 | 2012-02-28 22:39:22 +0000 | [diff] [blame] | 2192 | // Allocation node, is the last node in the current context in which the | 
|  | 2193 | // symbol was tracked. | 
|  | 2194 | if (N->getLocationContext() == LeakContext) | 
|  | 2195 | Last = N; | 
|  | 2196 |  | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2197 | N = N->pred_empty() ? NULL : *(N->pred_begin()); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2198 | } | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2199 |  | 
| Anna Zaks | 75de323 | 2012-02-28 22:39:22 +0000 | [diff] [blame] | 2200 | // If allocation happened in a function different from the leak node context, | 
|  | 2201 | // do not report the binding. | 
|  | 2202 | if (N->getLocationContext() != LeakContext) { | 
|  | 2203 | FirstBinding = 0; | 
|  | 2204 | } | 
|  | 2205 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2206 | return std::make_pair(Last, FirstBinding); | 
|  | 2207 | } | 
|  | 2208 |  | 
|  | 2209 | PathDiagnosticPiece* | 
| Anna Zaks | 88255cc | 2011-08-20 01:27:22 +0000 | [diff] [blame] | 2210 | CFRefReportVisitor::getEndPath(BugReporterContext &BRC, | 
|  | 2211 | const ExplodedNode *EndN, | 
|  | 2212 | BugReport &BR) { | 
| Ted Kremenek | 1e809b4 | 2012-03-09 01:13:14 +0000 | [diff] [blame] | 2213 | BR.markInteresting(Sym); | 
| Anna Zaks | 88255cc | 2011-08-20 01:27:22 +0000 | [diff] [blame] | 2214 | return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2215 | } | 
|  | 2216 |  | 
|  | 2217 | PathDiagnosticPiece* | 
| Anna Zaks | 88255cc | 2011-08-20 01:27:22 +0000 | [diff] [blame] | 2218 | CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC, | 
|  | 2219 | const ExplodedNode *EndN, | 
|  | 2220 | BugReport &BR) { | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2221 |  | 
| Ted Kremenek | bb8d546 | 2009-05-06 21:39:49 +0000 | [diff] [blame] | 2222 | // Tell the BugReporterContext to report cases when the tracked symbol is | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2223 | // assigned to different variables, etc. | 
| Ted Kremenek | 1e809b4 | 2012-03-09 01:13:14 +0000 | [diff] [blame] | 2224 | BR.markInteresting(Sym); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2225 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2226 | // We are reporting a leak.  Walk up the graph to get to the first node where | 
|  | 2227 | // the symbol appeared, and also get the first VarDecl that tracked object | 
|  | 2228 | // is stored to. | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 2229 | const ExplodedNode *AllocNode = 0; | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2230 | const MemRegion* FirstBinding = 0; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2231 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2232 | llvm::tie(AllocNode, FirstBinding) = | 
| Ted Kremenek | 8c8fb48 | 2009-05-08 23:32:51 +0000 | [diff] [blame] | 2233 | GetAllocationSite(BRC.getStateManager(), EndN, Sym); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2234 |  | 
| Anna Zaks | 921f049 | 2011-09-15 18:56:07 +0000 | [diff] [blame] | 2235 | SourceManager& SM = BRC.getSourceManager(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2236 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2237 | // Compute an actual location for the leak.  Sometimes a leak doesn't | 
|  | 2238 | // occur at an actual statement (e.g., transition between blocks; end | 
|  | 2239 | // of function) so we need to walk the graph and compute a real location. | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 2240 | const ExplodedNode *LeakN = EndN; | 
| Anna Zaks | 921f049 | 2011-09-15 18:56:07 +0000 | [diff] [blame] | 2241 | PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2242 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2243 | std::string sbuf; | 
|  | 2244 | llvm::raw_string_ostream os(sbuf); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2245 |  | 
| Ted Kremenek | f230198 | 2011-05-26 18:45:44 +0000 | [diff] [blame] | 2246 | os << "Object leaked: "; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2247 |  | 
| Ted Kremenek | f230198 | 2011-05-26 18:45:44 +0000 | [diff] [blame] | 2248 | if (FirstBinding) { | 
|  | 2249 | os << "object allocated and stored into '" | 
|  | 2250 | << FirstBinding->getString() << '\''; | 
|  | 2251 | } | 
|  | 2252 | else | 
|  | 2253 | os << "allocated object"; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2254 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2255 | // Get the retain count. | 
|  | 2256 | const RefVal* RV = EndN->getState()->get<RefBindings>(Sym); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2257 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2258 | if (RV->getKind() == RefVal::ErrorLeakReturned) { | 
|  | 2259 | // FIXME: Per comments in rdar://6320065, "create" only applies to CF | 
| Jordy Rose | 43426f8 | 2011-07-15 22:17:54 +0000 | [diff] [blame] | 2260 | // objects.  Only "copy", "alloc", "retain" and "new" transfer ownership | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2261 | // to the caller for NS objects. | 
| Ted Kremenek | 8e2c9b0 | 2011-05-25 06:19:45 +0000 | [diff] [blame] | 2262 | const Decl *D = &EndN->getCodeDecl(); | 
|  | 2263 | if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { | 
|  | 2264 | os << " is returned from a method whose name ('" | 
|  | 2265 | << MD->getSelector().getAsString() | 
|  | 2266 | << "') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'." | 
| Jordy Rose | 43426f8 | 2011-07-15 22:17:54 +0000 | [diff] [blame] | 2267 | "  This violates the naming convention rules" | 
| Ted Kremenek | f230198 | 2011-05-26 18:45:44 +0000 | [diff] [blame] | 2268 | " given in the Memory Management Guide for Cocoa"; | 
| Ted Kremenek | 8e2c9b0 | 2011-05-25 06:19:45 +0000 | [diff] [blame] | 2269 | } | 
|  | 2270 | else { | 
|  | 2271 | const FunctionDecl *FD = cast<FunctionDecl>(D); | 
| Ted Kremenek | 5586354 | 2011-12-22 06:35:52 +0000 | [diff] [blame] | 2272 | os << " is returned from a function whose name ('" | 
| Benjamin Kramer | db0fc51 | 2012-02-07 11:57:57 +0000 | [diff] [blame] | 2273 | << *FD | 
| Ted Kremenek | 8e2c9b0 | 2011-05-25 06:19:45 +0000 | [diff] [blame] | 2274 | << "') does not contain 'Copy' or 'Create'.  This violates the naming" | 
| Ted Kremenek | 5586354 | 2011-12-22 06:35:52 +0000 | [diff] [blame] | 2275 | " convention rules given in the Memory Management Guide for Core" | 
| Ted Kremenek | f230198 | 2011-05-26 18:45:44 +0000 | [diff] [blame] | 2276 | " Foundation"; | 
| Ted Kremenek | 8e2c9b0 | 2011-05-25 06:19:45 +0000 | [diff] [blame] | 2277 | } | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2278 | } | 
| Ted Kremenek | dee56e3 | 2009-05-10 06:25:57 +0000 | [diff] [blame] | 2279 | else if (RV->getKind() == RefVal::ErrorGCLeakReturned) { | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 2280 | ObjCMethodDecl &MD = cast<ObjCMethodDecl>(EndN->getCodeDecl()); | 
| Ted Kremenek | dee56e3 | 2009-05-10 06:25:57 +0000 | [diff] [blame] | 2281 | os << " and returned from method '" << MD.getSelector().getAsString() | 
| Ted Kremenek | 1f8e434 | 2009-05-10 16:52:15 +0000 | [diff] [blame] | 2282 | << "' is potentially leaked when using garbage collection.  Callers " | 
|  | 2283 | "of this method do not expect a returned object with a +1 retain " | 
|  | 2284 | "count since they expect the object to be managed by the garbage " | 
|  | 2285 | "collector"; | 
| Ted Kremenek | dee56e3 | 2009-05-10 06:25:57 +0000 | [diff] [blame] | 2286 | } | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2287 | else | 
| Ted Kremenek | 4f63ac7 | 2010-10-15 22:50:23 +0000 | [diff] [blame] | 2288 | os << " is not referenced later in this execution path and has a retain " | 
| Ted Kremenek | f230198 | 2011-05-26 18:45:44 +0000 | [diff] [blame] | 2289 | "count of +" << RV->getCount(); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2290 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2291 | return new PathDiagnosticEventPiece(L, os.str()); | 
|  | 2292 | } | 
|  | 2293 |  | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 2294 | CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts, | 
|  | 2295 | bool GCEnabled, const SummaryLogTy &Log, | 
|  | 2296 | ExplodedNode *n, SymbolRef sym, | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 2297 | CheckerContext &Ctx) | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 2298 | : CFRefReport(D, LOpts, GCEnabled, Log, n, sym, false) { | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2299 |  | 
| Chris Lattner | 57540c5 | 2011-04-15 05:22:18 +0000 | [diff] [blame] | 2300 | // Most bug reports are cached at the location where they occurred. | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2301 | // With leaks, we want to unique them by the location where they were | 
|  | 2302 | // allocated, and only report a single path.  To do this, we need to find | 
|  | 2303 | // the allocation site of a piece of tracked memory, which we do via a | 
|  | 2304 | // call to GetAllocationSite.  This will walk the ExplodedGraph backwards. | 
|  | 2305 | // Note that this is *not* the trimmed graph; we are guaranteed, however, | 
|  | 2306 | // that all ancestor nodes that represent the allocation site have the | 
|  | 2307 | // same SourceLocation. | 
| Ted Kremenek | 5ef32db | 2011-08-12 23:37:29 +0000 | [diff] [blame] | 2308 | const ExplodedNode *AllocNode = 0; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2309 |  | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 2310 | const SourceManager& SMgr = Ctx.getSourceManager(); | 
| Anna Zaks | c29bed3 | 2011-09-20 21:38:35 +0000 | [diff] [blame] | 2311 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2312 | llvm::tie(AllocNode, AllocBinding) =  // Set AllocBinding. | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 2313 | GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2314 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2315 | // Get the SourceLocation for the allocation site. | 
|  | 2316 | ProgramPoint P = AllocNode->getLocation(); | 
| Anna Zaks | c29bed3 | 2011-09-20 21:38:35 +0000 | [diff] [blame] | 2317 | const Stmt *AllocStmt = cast<PostStmt>(P).getStmt(); | 
|  | 2318 | Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr, | 
|  | 2319 | n->getLocationContext()); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2320 | // Fill in the description of the bug. | 
|  | 2321 | Description.clear(); | 
|  | 2322 | llvm::raw_string_ostream os(Description); | 
| Ted Kremenek | f1e7667 | 2009-05-02 19:05:19 +0000 | [diff] [blame] | 2323 | os << "Potential leak "; | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 2324 | if (GCEnabled) | 
| Ted Kremenek | f1e7667 | 2009-05-02 19:05:19 +0000 | [diff] [blame] | 2325 | os << "(when using garbage collection) "; | 
| Anna Zaks | 16f3831 | 2012-02-28 21:49:08 +0000 | [diff] [blame] | 2326 | os << "of an object"; | 
| Mike Stump | 11289f4 | 2009-09-09 15:08:12 +0000 | [diff] [blame] | 2327 |  | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2328 | // FIXME: AllocBinding doesn't get populated for RegionStore yet. | 
|  | 2329 | if (AllocBinding) | 
| Anna Zaks | 16f3831 | 2012-02-28 21:49:08 +0000 | [diff] [blame] | 2330 | os << " stored into '" << AllocBinding->getString() << '\''; | 
| Anna Zaks | 071a89c | 2011-08-19 23:21:56 +0000 | [diff] [blame] | 2331 |  | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 2332 | addVisitor(new CFRefLeakReportVisitor(sym, GCEnabled, Log)); | 
| Ted Kremenek | 6bd7870 | 2009-04-29 18:50:19 +0000 | [diff] [blame] | 2333 | } | 
|  | 2334 |  | 
|  | 2335 | //===----------------------------------------------------------------------===// | 
|  | 2336 | // Main checker logic. | 
|  | 2337 | //===----------------------------------------------------------------------===// | 
|  | 2338 |  | 
| Ted Kremenek | 70a8788 | 2009-11-25 22:17:44 +0000 | [diff] [blame] | 2339 | namespace { | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2340 | class RetainCountChecker | 
| Jordy Rose | 5df640d | 2011-08-24 18:56:32 +0000 | [diff] [blame] | 2341 | : public Checker< check::Bind, | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2342 | check::DeadSymbols, | 
| Jordy Rose | 5df640d | 2011-08-24 18:56:32 +0000 | [diff] [blame] | 2343 | check::EndAnalysis, | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2344 | check::EndPath, | 
| Jordy Rose | 217eb90 | 2011-08-17 21:27:39 +0000 | [diff] [blame] | 2345 | check::PostStmt<BlockExpr>, | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 2346 | check::PostStmt<CastExpr>, | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2347 | check::PostStmt<CallExpr>, | 
| Jordy Rose | 1fad663 | 2011-08-27 22:51:26 +0000 | [diff] [blame] | 2348 | check::PostStmt<CXXConstructExpr>, | 
| Ted Kremenek | 415287d | 2012-03-06 20:06:12 +0000 | [diff] [blame] | 2349 | check::PostStmt<ObjCArrayLiteral>, | 
|  | 2350 | check::PostStmt<ObjCDictionaryLiteral>, | 
| Jordy Rose | 6393f82 | 2012-05-12 05:10:43 +0000 | [diff] [blame] | 2351 | check::PostStmt<ObjCBoxedExpr>, | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2352 | check::PostObjCMessage, | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 2353 | check::PreStmt<ReturnStmt>, | 
| Jordy Rose | 217eb90 | 2011-08-17 21:27:39 +0000 | [diff] [blame] | 2354 | check::RegionChanges, | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 2355 | eval::Assume, | 
|  | 2356 | eval::Call > { | 
| Dylan Noblesmith | e277899 | 2012-02-05 02:12:40 +0000 | [diff] [blame] | 2357 | mutable OwningPtr<CFRefBug> useAfterRelease, releaseNotOwned; | 
|  | 2358 | mutable OwningPtr<CFRefBug> deallocGC, deallocNotOwned; | 
|  | 2359 | mutable OwningPtr<CFRefBug> overAutorelease, returnNotOwnedForOwned; | 
|  | 2360 | mutable OwningPtr<CFRefBug> leakWithinFunction, leakAtReturn; | 
|  | 2361 | mutable OwningPtr<CFRefBug> leakWithinFunctionGC, leakAtReturnGC; | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2362 |  | 
|  | 2363 | typedef llvm::DenseMap<SymbolRef, const SimpleProgramPointTag *> SymbolTagMap; | 
|  | 2364 |  | 
|  | 2365 | // This map is only used to ensure proper deletion of any allocated tags. | 
|  | 2366 | mutable SymbolTagMap DeadSymbolTags; | 
|  | 2367 |  | 
| Dylan Noblesmith | e277899 | 2012-02-05 02:12:40 +0000 | [diff] [blame] | 2368 | mutable OwningPtr<RetainSummaryManager> Summaries; | 
|  | 2369 | mutable OwningPtr<RetainSummaryManager> SummariesGC; | 
| Jordy Rose | 8b289a2 | 2011-08-25 00:10:37 +0000 | [diff] [blame] | 2370 |  | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2371 | mutable ARCounts::Factory ARCountFactory; | 
|  | 2372 |  | 
| Jordy Rose | 5df640d | 2011-08-24 18:56:32 +0000 | [diff] [blame] | 2373 | mutable SummaryLogTy SummaryLog; | 
|  | 2374 | mutable bool ShouldResetSummaryLog; | 
|  | 2375 |  | 
| Jordy Rose | a8f99ba | 2011-08-20 21:17:59 +0000 | [diff] [blame] | 2376 | public: | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2377 | RetainCountChecker() : ShouldResetSummaryLog(false) {} | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2378 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2379 | virtual ~RetainCountChecker() { | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2380 | DeleteContainerSeconds(DeadSymbolTags); | 
|  | 2381 | } | 
|  | 2382 |  | 
| Jordy Rose | 5df640d | 2011-08-24 18:56:32 +0000 | [diff] [blame] | 2383 | void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, | 
|  | 2384 | ExprEngine &Eng) const { | 
|  | 2385 | // FIXME: This is a hack to make sure the summary log gets cleared between | 
|  | 2386 | // analyses of different code bodies. | 
|  | 2387 | // | 
|  | 2388 | // Why is this necessary? Because a checker's lifetime is tied to a | 
|  | 2389 | // translation unit, but an ExplodedGraph's lifetime is just a code body. | 
|  | 2390 | // Once in a blue moon, a new ExplodedNode will have the same address as an | 
|  | 2391 | // old one with an associated summary, and the bug report visitor gets very | 
|  | 2392 | // confused. (To make things worse, the summary lifetime is currently also | 
|  | 2393 | // tied to a code body, so we get a crash instead of incorrect results.) | 
| Jordy Rose | 95589f1 | 2011-08-24 09:27:24 +0000 | [diff] [blame] | 2394 | // | 
|  | 2395 | // Why is this a bad solution? Because if the lifetime of the ExplodedGraph | 
|  | 2396 | // changes, things will start going wrong again. Really the lifetime of this | 
|  | 2397 | // log needs to be tied to either the specific nodes in it or the entire | 
|  | 2398 | // ExplodedGraph, not to a specific part of the code being analyzed. | 
|  | 2399 | // | 
| Jordy Rose | 5df640d | 2011-08-24 18:56:32 +0000 | [diff] [blame] | 2400 | // (Also, having stateful local data means that the same checker can't be | 
|  | 2401 | // used from multiple threads, but a lot of checkers have incorrect | 
|  | 2402 | // assumptions about that anyway. So that wasn't a priority at the time of | 
|  | 2403 | // this fix.) | 
| Jordy Rose | 95589f1 | 2011-08-24 09:27:24 +0000 | [diff] [blame] | 2404 | // | 
| Jordy Rose | 5df640d | 2011-08-24 18:56:32 +0000 | [diff] [blame] | 2405 | // This happens at the end of analysis, but bug reports are emitted /after/ | 
|  | 2406 | // this point. So we can't just clear the summary log now. Instead, we mark | 
|  | 2407 | // that the next time we access the summary log, it should be cleared. | 
|  | 2408 |  | 
|  | 2409 | // If we never reset the summary log during /this/ code body analysis, | 
|  | 2410 | // there were no new summaries. There might still have been summaries from | 
|  | 2411 | // the /last/ analysis, so clear them out to make sure the bug report | 
|  | 2412 | // visitors don't get confused. | 
|  | 2413 | if (ShouldResetSummaryLog) | 
|  | 2414 | SummaryLog.clear(); | 
|  | 2415 |  | 
|  | 2416 | ShouldResetSummaryLog = !SummaryLog.empty(); | 
| Jordy Rose | 95589f1 | 2011-08-24 09:27:24 +0000 | [diff] [blame] | 2417 | } | 
|  | 2418 |  | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2419 | CFRefBug *getLeakWithinFunctionBug(const LangOptions &LOpts, | 
|  | 2420 | bool GCEnabled) const { | 
|  | 2421 | if (GCEnabled) { | 
| Jordy Rose | 15484da | 2011-08-25 01:14:38 +0000 | [diff] [blame] | 2422 | if (!leakWithinFunctionGC) | 
|  | 2423 | leakWithinFunctionGC.reset(new LeakWithinFunction("Leak of object when " | 
|  | 2424 | "using garbage " | 
|  | 2425 | "collection")); | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2426 | return leakWithinFunctionGC.get(); | 
| Jordy Rose | 15484da | 2011-08-25 01:14:38 +0000 | [diff] [blame] | 2427 | } else { | 
|  | 2428 | if (!leakWithinFunction) { | 
| Douglas Gregor | 79a9141 | 2011-09-13 17:21:33 +0000 | [diff] [blame] | 2429 | if (LOpts.getGC() == LangOptions::HybridGC) { | 
| Jordy Rose | 15484da | 2011-08-25 01:14:38 +0000 | [diff] [blame] | 2430 | leakWithinFunction.reset(new LeakWithinFunction("Leak of object when " | 
|  | 2431 | "not using garbage " | 
|  | 2432 | "collection (GC) in " | 
|  | 2433 | "dual GC/non-GC " | 
|  | 2434 | "code")); | 
|  | 2435 | } else { | 
|  | 2436 | leakWithinFunction.reset(new LeakWithinFunction("Leak")); | 
|  | 2437 | } | 
|  | 2438 | } | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2439 | return leakWithinFunction.get(); | 
| Jordy Rose | 15484da | 2011-08-25 01:14:38 +0000 | [diff] [blame] | 2440 | } | 
|  | 2441 | } | 
|  | 2442 |  | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2443 | CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const { | 
|  | 2444 | if (GCEnabled) { | 
| Jordy Rose | 15484da | 2011-08-25 01:14:38 +0000 | [diff] [blame] | 2445 | if (!leakAtReturnGC) | 
|  | 2446 | leakAtReturnGC.reset(new LeakAtReturn("Leak of returned object when " | 
|  | 2447 | "using garbage collection")); | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2448 | return leakAtReturnGC.get(); | 
| Jordy Rose | 15484da | 2011-08-25 01:14:38 +0000 | [diff] [blame] | 2449 | } else { | 
|  | 2450 | if (!leakAtReturn) { | 
| Douglas Gregor | 79a9141 | 2011-09-13 17:21:33 +0000 | [diff] [blame] | 2451 | if (LOpts.getGC() == LangOptions::HybridGC) { | 
| Jordy Rose | 15484da | 2011-08-25 01:14:38 +0000 | [diff] [blame] | 2452 | leakAtReturn.reset(new LeakAtReturn("Leak of returned object when " | 
|  | 2453 | "not using garbage collection " | 
|  | 2454 | "(GC) in dual GC/non-GC code")); | 
|  | 2455 | } else { | 
|  | 2456 | leakAtReturn.reset(new LeakAtReturn("Leak of returned object")); | 
|  | 2457 | } | 
|  | 2458 | } | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2459 | return leakAtReturn.get(); | 
| Jordy Rose | 15484da | 2011-08-25 01:14:38 +0000 | [diff] [blame] | 2460 | } | 
|  | 2461 | } | 
|  | 2462 |  | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2463 | RetainSummaryManager &getSummaryManager(ASTContext &Ctx, | 
|  | 2464 | bool GCEnabled) const { | 
|  | 2465 | // FIXME: We don't support ARC being turned on and off during one analysis. | 
|  | 2466 | // (nor, for that matter, do we support changing ASTContexts) | 
| David Blaikie | bbafb8a | 2012-03-11 07:00:24 +0000 | [diff] [blame] | 2467 | bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount; | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2468 | if (GCEnabled) { | 
|  | 2469 | if (!SummariesGC) | 
| Jordy Rose | 8b289a2 | 2011-08-25 00:10:37 +0000 | [diff] [blame] | 2470 | SummariesGC.reset(new RetainSummaryManager(Ctx, true, ARCEnabled)); | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2471 | else | 
|  | 2472 | assert(SummariesGC->isARCEnabled() == ARCEnabled); | 
| Jordy Rose | 8b289a2 | 2011-08-25 00:10:37 +0000 | [diff] [blame] | 2473 | return *SummariesGC; | 
|  | 2474 | } else { | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2475 | if (!Summaries) | 
| Jordy Rose | 8b289a2 | 2011-08-25 00:10:37 +0000 | [diff] [blame] | 2476 | Summaries.reset(new RetainSummaryManager(Ctx, false, ARCEnabled)); | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2477 | else | 
|  | 2478 | assert(Summaries->isARCEnabled() == ARCEnabled); | 
| Jordy Rose | 8b289a2 | 2011-08-25 00:10:37 +0000 | [diff] [blame] | 2479 | return *Summaries; | 
|  | 2480 | } | 
|  | 2481 | } | 
|  | 2482 |  | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2483 | RetainSummaryManager &getSummaryManager(CheckerContext &C) const { | 
|  | 2484 | return getSummaryManager(C.getASTContext(), C.isObjCGCEnabled()); | 
|  | 2485 | } | 
|  | 2486 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2487 | void printState(raw_ostream &Out, ProgramStateRef State, | 
| Jordy Rose | 58a20d3 | 2011-08-28 19:11:56 +0000 | [diff] [blame] | 2488 | const char *NL, const char *Sep) const; | 
|  | 2489 |  | 
| Anna Zaks | 3e0f415 | 2011-10-06 00:43:15 +0000 | [diff] [blame] | 2490 | void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; | 
| Jordy Rose | 5c252ef | 2011-08-20 21:16:58 +0000 | [diff] [blame] | 2491 | void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; | 
|  | 2492 | void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 2493 |  | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2494 | void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; | 
| Jordy Rose | 1fad663 | 2011-08-27 22:51:26 +0000 | [diff] [blame] | 2495 | void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const; | 
| Ted Kremenek | 415287d | 2012-03-06 20:06:12 +0000 | [diff] [blame] | 2496 | void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const; | 
|  | 2497 | void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const; | 
| Jordy Rose | 6393f82 | 2012-05-12 05:10:43 +0000 | [diff] [blame] | 2498 | void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const; | 
|  | 2499 |  | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2500 | void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const; | 
| Ted Kremenek | 415287d | 2012-03-06 20:06:12 +0000 | [diff] [blame] | 2501 |  | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2502 | void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call, | 
| Jordy Rose | d188d66 | 2011-08-28 05:16:28 +0000 | [diff] [blame] | 2503 | CheckerContext &C) const; | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2504 |  | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 2505 | bool evalCall(const CallExpr *CE, CheckerContext &C) const; | 
|  | 2506 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2507 | ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, | 
| Jordy Rose | 5c252ef | 2011-08-20 21:16:58 +0000 | [diff] [blame] | 2508 | bool Assumption) const; | 
| Jordy Rose | 217eb90 | 2011-08-17 21:27:39 +0000 | [diff] [blame] | 2509 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2510 | ProgramStateRef | 
|  | 2511 | checkRegionChanges(ProgramStateRef state, | 
| Jordy Rose | 1fad663 | 2011-08-27 22:51:26 +0000 | [diff] [blame] | 2512 | const StoreManager::InvalidatedSymbols *invalidated, | 
|  | 2513 | ArrayRef<const MemRegion *> ExplicitRegions, | 
| Anna Zaks | 3d34834 | 2012-02-14 21:55:24 +0000 | [diff] [blame] | 2514 | ArrayRef<const MemRegion *> Regions, | 
|  | 2515 | const CallOrObjCMessage *Call) const; | 
| Jordy Rose | 5c252ef | 2011-08-20 21:16:58 +0000 | [diff] [blame] | 2516 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2517 | bool wantsRegionChangeUpdate(ProgramStateRef state) const { | 
| Jordy Rose | a8f99ba | 2011-08-20 21:17:59 +0000 | [diff] [blame] | 2518 | return true; | 
| Jordy Rose | 5c252ef | 2011-08-20 21:16:58 +0000 | [diff] [blame] | 2519 | } | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2520 |  | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 2521 | void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; | 
|  | 2522 | void checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C, | 
|  | 2523 | ExplodedNode *Pred, RetEffect RE, RefVal X, | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2524 | SymbolRef Sym, ProgramStateRef state) const; | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 2525 |  | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2526 | void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; | 
| Anna Zaks | 3eae334 | 2011-10-25 19:56:48 +0000 | [diff] [blame] | 2527 | void checkEndPath(CheckerContext &C) const; | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2528 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2529 | ProgramStateRef updateSymbol(ProgramStateRef state, SymbolRef sym, | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2530 | RefVal V, ArgEffect E, RefVal::Kind &hasErr, | 
|  | 2531 | CheckerContext &C) const; | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2532 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2533 | void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2534 | RefVal::Kind ErrorKind, SymbolRef Sym, | 
|  | 2535 | CheckerContext &C) const; | 
| Ted Kremenek | 415287d | 2012-03-06 20:06:12 +0000 | [diff] [blame] | 2536 |  | 
|  | 2537 | void processObjCLiterals(CheckerContext &C, const Expr *Ex) const; | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2538 |  | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2539 | const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const; | 
|  | 2540 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2541 | ProgramStateRef handleSymbolDeath(ProgramStateRef state, | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2542 | SymbolRef sid, RefVal V, | 
|  | 2543 | SmallVectorImpl<SymbolRef> &Leaked) const; | 
|  | 2544 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2545 | std::pair<ExplodedNode *, ProgramStateRef > | 
|  | 2546 | handleAutoreleaseCounts(ProgramStateRef state, | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 2547 | GenericNodeBuilderRefCount Bd, ExplodedNode *Pred, | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 2548 | CheckerContext &Ctx, SymbolRef Sym, RefVal V) const; | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 2549 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2550 | ExplodedNode *processLeaks(ProgramStateRef state, | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2551 | SmallVectorImpl<SymbolRef> &Leaked, | 
|  | 2552 | GenericNodeBuilderRefCount &Builder, | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 2553 | CheckerContext &Ctx, | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 2554 | ExplodedNode *Pred = 0) const; | 
| Ted Kremenek | 70a8788 | 2009-11-25 22:17:44 +0000 | [diff] [blame] | 2555 | }; | 
|  | 2556 | } // end anonymous namespace | 
|  | 2557 |  | 
| Jordy Rose | 217eb90 | 2011-08-17 21:27:39 +0000 | [diff] [blame] | 2558 | namespace { | 
|  | 2559 | class StopTrackingCallback : public SymbolVisitor { | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2560 | ProgramStateRef state; | 
| Jordy Rose | 217eb90 | 2011-08-17 21:27:39 +0000 | [diff] [blame] | 2561 | public: | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2562 | StopTrackingCallback(ProgramStateRef st) : state(st) {} | 
|  | 2563 | ProgramStateRef getState() const { return state; } | 
| Jordy Rose | 217eb90 | 2011-08-17 21:27:39 +0000 | [diff] [blame] | 2564 |  | 
|  | 2565 | bool VisitSymbol(SymbolRef sym) { | 
|  | 2566 | state = state->remove<RefBindings>(sym); | 
|  | 2567 | return true; | 
|  | 2568 | } | 
|  | 2569 | }; | 
|  | 2570 | } // end anonymous namespace | 
|  | 2571 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2572 | //===----------------------------------------------------------------------===// | 
|  | 2573 | // Handle statements that may have an effect on refcounts. | 
|  | 2574 | //===----------------------------------------------------------------------===// | 
| Jordy Rose | 217eb90 | 2011-08-17 21:27:39 +0000 | [diff] [blame] | 2575 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2576 | void RetainCountChecker::checkPostStmt(const BlockExpr *BE, | 
|  | 2577 | CheckerContext &C) const { | 
| Jordy Rose | 217eb90 | 2011-08-17 21:27:39 +0000 | [diff] [blame] | 2578 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2579 | // Scan the BlockDecRefExprs for any object the retain count checker | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 2580 | // may be tracking. | 
| John McCall | c63de66 | 2011-02-02 13:00:07 +0000 | [diff] [blame] | 2581 | if (!BE->getBlockDecl()->hasCaptures()) | 
| Ted Kremenek | f89dcda | 2009-11-26 02:38:19 +0000 | [diff] [blame] | 2582 | return; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 2583 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2584 | ProgramStateRef state = C.getState(); | 
| Ted Kremenek | f89dcda | 2009-11-26 02:38:19 +0000 | [diff] [blame] | 2585 | const BlockDataRegion *R = | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2586 | cast<BlockDataRegion>(state->getSVal(BE, | 
|  | 2587 | C.getLocationContext()).getAsRegion()); | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 2588 |  | 
| Ted Kremenek | f89dcda | 2009-11-26 02:38:19 +0000 | [diff] [blame] | 2589 | BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), | 
|  | 2590 | E = R->referenced_vars_end(); | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 2591 |  | 
| Ted Kremenek | f89dcda | 2009-11-26 02:38:19 +0000 | [diff] [blame] | 2592 | if (I == E) | 
|  | 2593 | return; | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 2594 |  | 
| Ted Kremenek | 04af9f2 | 2009-12-07 22:05:27 +0000 | [diff] [blame] | 2595 | // FIXME: For now we invalidate the tracking of all symbols passed to blocks | 
|  | 2596 | // via captured variables, even though captured variables result in a copy | 
|  | 2597 | // and in implicit increment/decrement of a retain count. | 
| Chris Lattner | 0e62c1c | 2011-07-23 10:55:15 +0000 | [diff] [blame] | 2598 | SmallVector<const MemRegion*, 10> Regions; | 
| Anna Zaks | c9abbe2 | 2011-10-26 21:06:44 +0000 | [diff] [blame] | 2599 | const LocationContext *LC = C.getLocationContext(); | 
| Ted Kremenek | 90af909 | 2010-12-02 07:49:45 +0000 | [diff] [blame] | 2600 | MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 2601 |  | 
| Ted Kremenek | 04af9f2 | 2009-12-07 22:05:27 +0000 | [diff] [blame] | 2602 | for ( ; I != E; ++I) { | 
|  | 2603 | const VarRegion *VR = *I; | 
|  | 2604 | if (VR->getSuperRegion() == R) { | 
|  | 2605 | VR = MemMgr.getVarRegion(VR->getDecl(), LC); | 
|  | 2606 | } | 
|  | 2607 | Regions.push_back(VR); | 
|  | 2608 | } | 
| Ted Kremenek | bd86271 | 2010-07-01 20:16:50 +0000 | [diff] [blame] | 2609 |  | 
| Ted Kremenek | 04af9f2 | 2009-12-07 22:05:27 +0000 | [diff] [blame] | 2610 | state = | 
|  | 2611 | state->scanReachableSymbols<StopTrackingCallback>(Regions.data(), | 
|  | 2612 | Regions.data() + Regions.size()).getState(); | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 2613 | C.addTransition(state); | 
| Ted Kremenek | f89dcda | 2009-11-26 02:38:19 +0000 | [diff] [blame] | 2614 | } | 
|  | 2615 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2616 | void RetainCountChecker::checkPostStmt(const CastExpr *CE, | 
|  | 2617 | CheckerContext &C) const { | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 2618 | const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE); | 
|  | 2619 | if (!BE) | 
|  | 2620 | return; | 
|  | 2621 |  | 
| John McCall | 640767f | 2011-06-17 06:50:50 +0000 | [diff] [blame] | 2622 | ArgEffect AE = IncRef; | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 2623 |  | 
|  | 2624 | switch (BE->getBridgeKind()) { | 
|  | 2625 | case clang::OBC_Bridge: | 
|  | 2626 | // Do nothing. | 
|  | 2627 | return; | 
|  | 2628 | case clang::OBC_BridgeRetained: | 
|  | 2629 | AE = IncRef; | 
|  | 2630 | break; | 
|  | 2631 | case clang::OBC_BridgeTransfer: | 
|  | 2632 | AE = DecRefBridgedTransfered; | 
|  | 2633 | break; | 
|  | 2634 | } | 
|  | 2635 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2636 | ProgramStateRef state = C.getState(); | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2637 | SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol(); | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 2638 | if (!Sym) | 
|  | 2639 | return; | 
|  | 2640 | const RefVal* T = state->get<RefBindings>(Sym); | 
|  | 2641 | if (!T) | 
|  | 2642 | return; | 
|  | 2643 |  | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 2644 | RefVal::Kind hasErr = (RefVal::Kind) 0; | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2645 | state = updateSymbol(state, Sym, *T, AE, hasErr, C); | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 2646 |  | 
|  | 2647 | if (hasErr) { | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2648 | // FIXME: If we get an error during a bridge cast, should we report it? | 
|  | 2649 | // Should we assert that there is no error? | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 2650 | return; | 
|  | 2651 | } | 
|  | 2652 |  | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 2653 | C.addTransition(state); | 
| John McCall | 31168b0 | 2011-06-15 23:02:42 +0000 | [diff] [blame] | 2654 | } | 
|  | 2655 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2656 | void RetainCountChecker::checkPostStmt(const CallExpr *CE, | 
|  | 2657 | CheckerContext &C) const { | 
| Ted Kremenek | 161046e | 2012-03-23 06:26:56 +0000 | [diff] [blame] | 2658 | if (C.wasInlined) | 
|  | 2659 | return; | 
|  | 2660 |  | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2661 | // Get the callee. | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2662 | ProgramStateRef state = C.getState(); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2663 | const Expr *Callee = CE->getCallee(); | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2664 | SVal L = state->getSVal(Callee, C.getLocationContext()); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2665 |  | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2666 | RetainSummaryManager &Summaries = getSummaryManager(C); | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 2667 | const RetainSummary *Summ = 0; | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2668 |  | 
|  | 2669 | // FIXME: Better support for blocks.  For now we stop tracking anything | 
|  | 2670 | // that is passed to blocks. | 
|  | 2671 | // FIXME: Need to handle variables that are "captured" by the block. | 
|  | 2672 | if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) { | 
|  | 2673 | Summ = Summaries.getPersistentStopSummary(); | 
|  | 2674 | } else if (const FunctionDecl *FD = L.getAsFunctionDecl()) { | 
| Anna Zaks | f4c5ea5 | 2012-05-04 22:18:39 +0000 | [diff] [blame] | 2675 | CallOrObjCMessage CME(CE, state, C.getLocationContext()); | 
|  | 2676 | Summ = Summaries.getSummary(FD, &CME); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2677 | } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) { | 
| Anna Zaks | f4c5ea5 | 2012-05-04 22:18:39 +0000 | [diff] [blame] | 2678 | if (const CXXMethodDecl *MD = me->getMethodDecl()) { | 
|  | 2679 | CallOrObjCMessage CME(CE, state, C.getLocationContext()); | 
|  | 2680 | Summ = Summaries.getSummary(MD, &CME); | 
|  | 2681 | } | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2682 | } | 
|  | 2683 |  | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2684 | if (!Summ) | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 2685 | Summ = Summaries.getDefaultSummary(); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2686 |  | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2687 | checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2688 | } | 
|  | 2689 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2690 | void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE, | 
|  | 2691 | CheckerContext &C) const { | 
| Jordy Rose | 1fad663 | 2011-08-27 22:51:26 +0000 | [diff] [blame] | 2692 | const CXXConstructorDecl *Ctor = CE->getConstructor(); | 
|  | 2693 | if (!Ctor) | 
|  | 2694 | return; | 
|  | 2695 |  | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2696 | RetainSummaryManager &Summaries = getSummaryManager(C); | 
| Anna Zaks | f4c5ea5 | 2012-05-04 22:18:39 +0000 | [diff] [blame] | 2697 | ProgramStateRef state = C.getState(); | 
|  | 2698 | CallOrObjCMessage CME(CE, state, C.getLocationContext()); | 
|  | 2699 | const RetainSummary *Summ = Summaries.getSummary(Ctor, &CME); | 
| Jordy Rose | 1fad663 | 2011-08-27 22:51:26 +0000 | [diff] [blame] | 2700 |  | 
|  | 2701 | // If we didn't get a summary, this constructor doesn't affect retain counts. | 
|  | 2702 | if (!Summ) | 
|  | 2703 | return; | 
|  | 2704 |  | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2705 | checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C); | 
| Jordy Rose | 1fad663 | 2011-08-27 22:51:26 +0000 | [diff] [blame] | 2706 | } | 
|  | 2707 |  | 
| Ted Kremenek | 415287d | 2012-03-06 20:06:12 +0000 | [diff] [blame] | 2708 | void RetainCountChecker::processObjCLiterals(CheckerContext &C, | 
|  | 2709 | const Expr *Ex) const { | 
|  | 2710 | ProgramStateRef state = C.getState(); | 
|  | 2711 | const ExplodedNode *pred = C.getPredecessor(); | 
|  | 2712 | for (Stmt::const_child_iterator it = Ex->child_begin(), et = Ex->child_end() ; | 
|  | 2713 | it != et ; ++it) { | 
|  | 2714 | const Stmt *child = *it; | 
|  | 2715 | SVal V = state->getSVal(child, pred->getLocationContext()); | 
|  | 2716 | if (SymbolRef sym = V.getAsSymbol()) | 
|  | 2717 | if (const RefVal* T = state->get<RefBindings>(sym)) { | 
|  | 2718 | RefVal::Kind hasErr = (RefVal::Kind) 0; | 
|  | 2719 | state = updateSymbol(state, sym, *T, MayEscape, hasErr, C); | 
|  | 2720 | if (hasErr) { | 
|  | 2721 | processNonLeakError(state, child->getSourceRange(), hasErr, sym, C); | 
|  | 2722 | return; | 
|  | 2723 | } | 
|  | 2724 | } | 
|  | 2725 | } | 
|  | 2726 |  | 
|  | 2727 | // Return the object as autoreleased. | 
|  | 2728 | //  RetEffect RE = RetEffect::MakeNotOwned(RetEffect::ObjC); | 
|  | 2729 | if (SymbolRef sym = | 
|  | 2730 | state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) { | 
|  | 2731 | QualType ResultTy = Ex->getType(); | 
|  | 2732 | state = state->set<RefBindings>(sym, RefVal::makeNotOwned(RetEffect::ObjC, | 
|  | 2733 | ResultTy)); | 
|  | 2734 | } | 
|  | 2735 |  | 
|  | 2736 | C.addTransition(state); | 
|  | 2737 | } | 
|  | 2738 |  | 
|  | 2739 | void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL, | 
|  | 2740 | CheckerContext &C) const { | 
|  | 2741 | // Apply the 'MayEscape' to all values. | 
|  | 2742 | processObjCLiterals(C, AL); | 
|  | 2743 | } | 
|  | 2744 |  | 
|  | 2745 | void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, | 
|  | 2746 | CheckerContext &C) const { | 
|  | 2747 | // Apply the 'MayEscape' to all keys and values. | 
|  | 2748 | processObjCLiterals(C, DL); | 
|  | 2749 | } | 
|  | 2750 |  | 
| Jordy Rose | 6393f82 | 2012-05-12 05:10:43 +0000 | [diff] [blame] | 2751 | void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex, | 
|  | 2752 | CheckerContext &C) const { | 
|  | 2753 | const ExplodedNode *Pred = C.getPredecessor(); | 
|  | 2754 | const LocationContext *LCtx = Pred->getLocationContext(); | 
|  | 2755 | ProgramStateRef State = Pred->getState(); | 
|  | 2756 |  | 
|  | 2757 | if (SymbolRef Sym = State->getSVal(Ex, LCtx).getAsSymbol()) { | 
|  | 2758 | QualType ResultTy = Ex->getType(); | 
|  | 2759 | State = State->set<RefBindings>(Sym, RefVal::makeNotOwned(RetEffect::ObjC, | 
|  | 2760 | ResultTy)); | 
|  | 2761 | } | 
|  | 2762 |  | 
|  | 2763 | C.addTransition(State); | 
|  | 2764 | } | 
|  | 2765 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2766 | void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg, | 
|  | 2767 | CheckerContext &C) const { | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2768 | ProgramStateRef state = C.getState(); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2769 |  | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2770 | RetainSummaryManager &Summaries = getSummaryManager(C); | 
| Jordy Rose | 8b289a2 | 2011-08-25 00:10:37 +0000 | [diff] [blame] | 2771 |  | 
| Ted Kremenek | f3e3f66 | 2011-10-05 23:54:29 +0000 | [diff] [blame] | 2772 | const RetainSummary *Summ; | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2773 | if (Msg.isInstanceMessage()) { | 
| Anna Zaks | 1c887b3 | 2011-11-01 22:41:01 +0000 | [diff] [blame] | 2774 | const LocationContext *LC = C.getLocationContext(); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2775 | Summ = Summaries.getInstanceMethodSummary(Msg, state, LC); | 
|  | 2776 | } else { | 
|  | 2777 | Summ = Summaries.getClassMethodSummary(Msg); | 
|  | 2778 | } | 
|  | 2779 |  | 
|  | 2780 | // If we didn't get a summary, this message doesn't affect retain counts. | 
|  | 2781 | if (!Summ) | 
|  | 2782 | return; | 
|  | 2783 |  | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2784 | checkSummary(*Summ, CallOrObjCMessage(Msg, state, C.getLocationContext()), C); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2785 | } | 
|  | 2786 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2787 | /// GetReturnType - Used to get the return type of a message expression or | 
|  | 2788 | ///  function call with the intention of affixing that type to a tracked symbol. | 
|  | 2789 | ///  While the the return type can be queried directly from RetEx, when | 
|  | 2790 | ///  invoking class methods we augment to the return type to be that of | 
|  | 2791 | ///  a pointer to the class (as opposed it just being id). | 
|  | 2792 | // FIXME: We may be able to do this with related result types instead. | 
|  | 2793 | // This function is probably overestimating. | 
|  | 2794 | static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) { | 
|  | 2795 | QualType RetTy = RetE->getType(); | 
|  | 2796 | // If RetE is not a message expression just return its type. | 
|  | 2797 | // If RetE is a message expression, return its types if it is something | 
|  | 2798 | /// more specific than id. | 
|  | 2799 | if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RetE)) | 
|  | 2800 | if (const ObjCObjectPointerType *PT = RetTy->getAs<ObjCObjectPointerType>()) | 
|  | 2801 | if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() || | 
|  | 2802 | PT->isObjCClassType()) { | 
|  | 2803 | // At this point we know the return type of the message expression is | 
|  | 2804 | // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this | 
|  | 2805 | // is a call to a class method whose type we can resolve.  In such | 
|  | 2806 | // cases, promote the return type to XXX* (where XXX is the class). | 
|  | 2807 | const ObjCInterfaceDecl *D = ME->getReceiverInterface(); | 
|  | 2808 | return !D ? RetTy : | 
|  | 2809 | Ctx.getObjCObjectPointerType(Ctx.getObjCInterfaceType(D)); | 
|  | 2810 | } | 
|  | 2811 |  | 
|  | 2812 | return RetTy; | 
|  | 2813 | } | 
|  | 2814 |  | 
|  | 2815 | void RetainCountChecker::checkSummary(const RetainSummary &Summ, | 
|  | 2816 | const CallOrObjCMessage &CallOrMsg, | 
|  | 2817 | CheckerContext &C) const { | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2818 | ProgramStateRef state = C.getState(); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2819 |  | 
|  | 2820 | // Evaluate the effect of the arguments. | 
|  | 2821 | RefVal::Kind hasErr = (RefVal::Kind) 0; | 
|  | 2822 | SourceRange ErrorRange; | 
|  | 2823 | SymbolRef ErrorSym = 0; | 
|  | 2824 |  | 
|  | 2825 | for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) { | 
| Jordy Rose | 1fad663 | 2011-08-27 22:51:26 +0000 | [diff] [blame] | 2826 | SVal V = CallOrMsg.getArgSVal(idx); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2827 |  | 
|  | 2828 | if (SymbolRef Sym = V.getAsLocSymbol()) { | 
|  | 2829 | if (RefBindings::data_type *T = state->get<RefBindings>(Sym)) { | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2830 | state = updateSymbol(state, Sym, *T, Summ.getArg(idx), hasErr, C); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2831 | if (hasErr) { | 
|  | 2832 | ErrorRange = CallOrMsg.getArgSourceRange(idx); | 
|  | 2833 | ErrorSym = Sym; | 
|  | 2834 | break; | 
|  | 2835 | } | 
|  | 2836 | } | 
|  | 2837 | } | 
|  | 2838 | } | 
|  | 2839 |  | 
|  | 2840 | // Evaluate the effect on the message receiver. | 
|  | 2841 | bool ReceiverIsTracked = false; | 
| Jordy Rose | d188d66 | 2011-08-28 05:16:28 +0000 | [diff] [blame] | 2842 | if (!hasErr && CallOrMsg.isObjCMessage()) { | 
| Anna Zaks | c9abbe2 | 2011-10-26 21:06:44 +0000 | [diff] [blame] | 2843 | const LocationContext *LC = C.getLocationContext(); | 
| Jordy Rose | d188d66 | 2011-08-28 05:16:28 +0000 | [diff] [blame] | 2844 | SVal Receiver = CallOrMsg.getInstanceMessageReceiver(LC); | 
|  | 2845 | if (SymbolRef Sym = Receiver.getAsLocSymbol()) { | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2846 | if (const RefVal *T = state->get<RefBindings>(Sym)) { | 
|  | 2847 | ReceiverIsTracked = true; | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2848 | state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(), | 
|  | 2849 | hasErr, C); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2850 | if (hasErr) { | 
| Jordy Rose | d188d66 | 2011-08-28 05:16:28 +0000 | [diff] [blame] | 2851 | ErrorRange = CallOrMsg.getReceiverSourceRange(); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2852 | ErrorSym = Sym; | 
|  | 2853 | } | 
|  | 2854 | } | 
|  | 2855 | } | 
|  | 2856 | } | 
|  | 2857 |  | 
|  | 2858 | // Process any errors. | 
|  | 2859 | if (hasErr) { | 
|  | 2860 | processNonLeakError(state, ErrorRange, hasErr, ErrorSym, C); | 
|  | 2861 | return; | 
|  | 2862 | } | 
|  | 2863 |  | 
|  | 2864 | // Consult the summary for the return value. | 
|  | 2865 | RetEffect RE = Summ.getRetEffect(); | 
|  | 2866 |  | 
|  | 2867 | if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) { | 
| Jordy Rose | 8b289a2 | 2011-08-25 00:10:37 +0000 | [diff] [blame] | 2868 | if (ReceiverIsTracked) | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2869 | RE = getSummaryManager(C).getObjAllocRetEffect(); | 
| Jordy Rose | 8b289a2 | 2011-08-25 00:10:37 +0000 | [diff] [blame] | 2870 | else | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2871 | RE = RetEffect::MakeNoRet(); | 
|  | 2872 | } | 
|  | 2873 |  | 
|  | 2874 | switch (RE.getKind()) { | 
|  | 2875 | default: | 
| David Blaikie | 8a40f70 | 2012-01-17 06:56:22 +0000 | [diff] [blame] | 2876 | llvm_unreachable("Unhandled RetEffect."); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2877 |  | 
|  | 2878 | case RetEffect::NoRet: | 
|  | 2879 | // No work necessary. | 
|  | 2880 | break; | 
|  | 2881 |  | 
|  | 2882 | case RetEffect::OwnedAllocatedSymbol: | 
|  | 2883 | case RetEffect::OwnedSymbol: { | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2884 | SymbolRef Sym = state->getSVal(CallOrMsg.getOriginExpr(), | 
|  | 2885 | C.getLocationContext()).getAsSymbol(); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2886 | if (!Sym) | 
|  | 2887 | break; | 
|  | 2888 |  | 
|  | 2889 | // Use the result type from callOrMsg as it automatically adjusts | 
|  | 2890 | // for methods/functions that return references. | 
|  | 2891 | QualType ResultTy = CallOrMsg.getResultType(C.getASTContext()); | 
|  | 2892 | state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(), | 
|  | 2893 | ResultTy)); | 
|  | 2894 |  | 
|  | 2895 | // FIXME: Add a flag to the checker where allocations are assumed to | 
|  | 2896 | // *not* fail. (The code below is out-of-date, though.) | 
|  | 2897 | #if 0 | 
|  | 2898 | if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) { | 
|  | 2899 | bool isFeasible; | 
|  | 2900 | state = state.assume(loc::SymbolVal(Sym), true, isFeasible); | 
|  | 2901 | assert(isFeasible && "Cannot assume fresh symbol is non-null."); | 
|  | 2902 | } | 
|  | 2903 | #endif | 
|  | 2904 |  | 
|  | 2905 | break; | 
|  | 2906 | } | 
|  | 2907 |  | 
|  | 2908 | case RetEffect::GCNotOwnedSymbol: | 
|  | 2909 | case RetEffect::ARCNotOwnedSymbol: | 
|  | 2910 | case RetEffect::NotOwnedSymbol: { | 
|  | 2911 | const Expr *Ex = CallOrMsg.getOriginExpr(); | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 2912 | SymbolRef Sym = state->getSVal(Ex, C.getLocationContext()).getAsSymbol(); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2913 | if (!Sym) | 
|  | 2914 | break; | 
|  | 2915 |  | 
|  | 2916 | // Use GetReturnType in order to give [NSFoo alloc] the type NSFoo *. | 
|  | 2917 | QualType ResultTy = GetReturnType(Ex, C.getASTContext()); | 
|  | 2918 | state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(), | 
|  | 2919 | ResultTy)); | 
|  | 2920 | break; | 
|  | 2921 | } | 
|  | 2922 | } | 
|  | 2923 |  | 
|  | 2924 | // This check is actually necessary; otherwise the statement builder thinks | 
|  | 2925 | // we've hit a previously-found path. | 
|  | 2926 | // Normally addTransition takes care of this, but we want the node pointer. | 
|  | 2927 | ExplodedNode *NewNode; | 
|  | 2928 | if (state == C.getState()) { | 
|  | 2929 | NewNode = C.getPredecessor(); | 
|  | 2930 | } else { | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 2931 | NewNode = C.addTransition(state); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2932 | } | 
|  | 2933 |  | 
| Jordy Rose | 5df640d | 2011-08-24 18:56:32 +0000 | [diff] [blame] | 2934 | // Annotate the node with summary we used. | 
|  | 2935 | if (NewNode) { | 
|  | 2936 | // FIXME: This is ugly. See checkEndAnalysis for why it's necessary. | 
|  | 2937 | if (ShouldResetSummaryLog) { | 
|  | 2938 | SummaryLog.clear(); | 
|  | 2939 | ShouldResetSummaryLog = false; | 
|  | 2940 | } | 
| Jordy Rose | 20d4e68 | 2011-08-23 20:55:48 +0000 | [diff] [blame] | 2941 | SummaryLog[NewNode] = &Summ; | 
| Jordy Rose | 5df640d | 2011-08-24 18:56:32 +0000 | [diff] [blame] | 2942 | } | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 2943 | } | 
|  | 2944 |  | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2945 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 2946 | ProgramStateRef | 
|  | 2947 | RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2948 | RefVal V, ArgEffect E, RefVal::Kind &hasErr, | 
|  | 2949 | CheckerContext &C) const { | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2950 | // In GC mode [... release] and [... retain] do nothing. | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 2951 | // In ARC mode they shouldn't exist at all, but we just ignore them. | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2952 | bool IgnoreRetainMsg = C.isObjCGCEnabled(); | 
|  | 2953 | if (!IgnoreRetainMsg) | 
| David Blaikie | bbafb8a | 2012-03-11 07:00:24 +0000 | [diff] [blame] | 2954 | IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount; | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2955 |  | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2956 | switch (E) { | 
|  | 2957 | default: break; | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2958 | case IncRefMsg: E = IgnoreRetainMsg ? DoNothing : IncRef; break; | 
|  | 2959 | case DecRefMsg: E = IgnoreRetainMsg ? DoNothing : DecRef; break; | 
|  | 2960 | case MakeCollectable: E = C.isObjCGCEnabled() ? DecRef : DoNothing; break; | 
|  | 2961 | case NewAutoreleasePool: E = C.isObjCGCEnabled() ? DoNothing : | 
|  | 2962 | NewAutoreleasePool; break; | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2963 | } | 
|  | 2964 |  | 
|  | 2965 | // Handle all use-after-releases. | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2966 | if (!C.isObjCGCEnabled() && V.getKind() == RefVal::Released) { | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2967 | V = V ^ RefVal::ErrorUseAfterRelease; | 
|  | 2968 | hasErr = V.getKind(); | 
|  | 2969 | return state->set<RefBindings>(sym, V); | 
|  | 2970 | } | 
|  | 2971 |  | 
|  | 2972 | switch (E) { | 
|  | 2973 | case DecRefMsg: | 
|  | 2974 | case IncRefMsg: | 
|  | 2975 | case MakeCollectable: | 
|  | 2976 | llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted"); | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2977 |  | 
|  | 2978 | case Dealloc: | 
|  | 2979 | // Any use of -dealloc in GC is *bad*. | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 2980 | if (C.isObjCGCEnabled()) { | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2981 | V = V ^ RefVal::ErrorDeallocGC; | 
|  | 2982 | hasErr = V.getKind(); | 
|  | 2983 | break; | 
|  | 2984 | } | 
|  | 2985 |  | 
|  | 2986 | switch (V.getKind()) { | 
|  | 2987 | default: | 
|  | 2988 | llvm_unreachable("Invalid RefVal state for an explicit dealloc."); | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 2989 | case RefVal::Owned: | 
|  | 2990 | // The object immediately transitions to the released state. | 
|  | 2991 | V = V ^ RefVal::Released; | 
|  | 2992 | V.clearCounts(); | 
|  | 2993 | return state->set<RefBindings>(sym, V); | 
|  | 2994 | case RefVal::NotOwned: | 
|  | 2995 | V = V ^ RefVal::ErrorDeallocNotOwned; | 
|  | 2996 | hasErr = V.getKind(); | 
|  | 2997 | break; | 
|  | 2998 | } | 
|  | 2999 | break; | 
|  | 3000 |  | 
|  | 3001 | case NewAutoreleasePool: | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3002 | assert(!C.isObjCGCEnabled()); | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 3003 | return state->add<AutoreleaseStack>(sym); | 
|  | 3004 |  | 
|  | 3005 | case MayEscape: | 
|  | 3006 | if (V.getKind() == RefVal::Owned) { | 
|  | 3007 | V = V ^ RefVal::NotOwned; | 
|  | 3008 | break; | 
|  | 3009 | } | 
|  | 3010 |  | 
|  | 3011 | // Fall-through. | 
|  | 3012 |  | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 3013 | case DoNothing: | 
|  | 3014 | return state; | 
|  | 3015 |  | 
|  | 3016 | case Autorelease: | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3017 | if (C.isObjCGCEnabled()) | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 3018 | return state; | 
|  | 3019 |  | 
|  | 3020 | // Update the autorelease counts. | 
|  | 3021 | state = SendAutorelease(state, ARCountFactory, sym); | 
|  | 3022 | V = V.autorelease(); | 
|  | 3023 | break; | 
|  | 3024 |  | 
|  | 3025 | case StopTracking: | 
|  | 3026 | return state->remove<RefBindings>(sym); | 
|  | 3027 |  | 
|  | 3028 | case IncRef: | 
|  | 3029 | switch (V.getKind()) { | 
|  | 3030 | default: | 
|  | 3031 | llvm_unreachable("Invalid RefVal state for a retain."); | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 3032 | case RefVal::Owned: | 
|  | 3033 | case RefVal::NotOwned: | 
|  | 3034 | V = V + 1; | 
|  | 3035 | break; | 
|  | 3036 | case RefVal::Released: | 
|  | 3037 | // Non-GC cases are handled above. | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3038 | assert(C.isObjCGCEnabled()); | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 3039 | V = (V ^ RefVal::Owned) + 1; | 
|  | 3040 | break; | 
|  | 3041 | } | 
|  | 3042 | break; | 
|  | 3043 |  | 
|  | 3044 | case SelfOwn: | 
|  | 3045 | V = V ^ RefVal::NotOwned; | 
|  | 3046 | // Fall-through. | 
|  | 3047 | case DecRef: | 
|  | 3048 | case DecRefBridgedTransfered: | 
|  | 3049 | switch (V.getKind()) { | 
|  | 3050 | default: | 
|  | 3051 | // case 'RefVal::Released' handled above. | 
|  | 3052 | llvm_unreachable("Invalid RefVal state for a release."); | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 3053 |  | 
|  | 3054 | case RefVal::Owned: | 
|  | 3055 | assert(V.getCount() > 0); | 
|  | 3056 | if (V.getCount() == 1) | 
|  | 3057 | V = V ^ (E == DecRefBridgedTransfered ? | 
|  | 3058 | RefVal::NotOwned : RefVal::Released); | 
|  | 3059 | V = V - 1; | 
|  | 3060 | break; | 
|  | 3061 |  | 
|  | 3062 | case RefVal::NotOwned: | 
|  | 3063 | if (V.getCount() > 0) | 
|  | 3064 | V = V - 1; | 
|  | 3065 | else { | 
|  | 3066 | V = V ^ RefVal::ErrorReleaseNotOwned; | 
|  | 3067 | hasErr = V.getKind(); | 
|  | 3068 | } | 
|  | 3069 | break; | 
|  | 3070 |  | 
|  | 3071 | case RefVal::Released: | 
|  | 3072 | // Non-GC cases are handled above. | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3073 | assert(C.isObjCGCEnabled()); | 
| Jordy Rose | bf77e51 | 2011-08-23 20:27:16 +0000 | [diff] [blame] | 3074 | V = V ^ RefVal::ErrorUseAfterRelease; | 
|  | 3075 | hasErr = V.getKind(); | 
|  | 3076 | break; | 
|  | 3077 | } | 
|  | 3078 | break; | 
|  | 3079 | } | 
|  | 3080 | return state->set<RefBindings>(sym, V); | 
|  | 3081 | } | 
|  | 3082 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3083 | void RetainCountChecker::processNonLeakError(ProgramStateRef St, | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3084 | SourceRange ErrorRange, | 
|  | 3085 | RefVal::Kind ErrorKind, | 
|  | 3086 | SymbolRef Sym, | 
|  | 3087 | CheckerContext &C) const { | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3088 | ExplodedNode *N = C.generateSink(St); | 
|  | 3089 | if (!N) | 
|  | 3090 | return; | 
|  | 3091 |  | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3092 | CFRefBug *BT; | 
|  | 3093 | switch (ErrorKind) { | 
|  | 3094 | default: | 
|  | 3095 | llvm_unreachable("Unhandled error."); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3096 | case RefVal::ErrorUseAfterRelease: | 
| Jordy Rose | 4ba0ba4 | 2011-08-25 00:34:03 +0000 | [diff] [blame] | 3097 | if (!useAfterRelease) | 
|  | 3098 | useAfterRelease.reset(new UseAfterRelease()); | 
|  | 3099 | BT = &*useAfterRelease; | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3100 | break; | 
|  | 3101 | case RefVal::ErrorReleaseNotOwned: | 
| Jordy Rose | 4ba0ba4 | 2011-08-25 00:34:03 +0000 | [diff] [blame] | 3102 | if (!releaseNotOwned) | 
|  | 3103 | releaseNotOwned.reset(new BadRelease()); | 
|  | 3104 | BT = &*releaseNotOwned; | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3105 | break; | 
|  | 3106 | case RefVal::ErrorDeallocGC: | 
| Jordy Rose | 4ba0ba4 | 2011-08-25 00:34:03 +0000 | [diff] [blame] | 3107 | if (!deallocGC) | 
|  | 3108 | deallocGC.reset(new DeallocGC()); | 
|  | 3109 | BT = &*deallocGC; | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3110 | break; | 
|  | 3111 | case RefVal::ErrorDeallocNotOwned: | 
| Jordy Rose | 4ba0ba4 | 2011-08-25 00:34:03 +0000 | [diff] [blame] | 3112 | if (!deallocNotOwned) | 
|  | 3113 | deallocNotOwned.reset(new DeallocNotOwned()); | 
|  | 3114 | BT = &*deallocNotOwned; | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3115 | break; | 
|  | 3116 | } | 
|  | 3117 |  | 
| Jordy Rose | 4ba0ba4 | 2011-08-25 00:34:03 +0000 | [diff] [blame] | 3118 | assert(BT); | 
| David Blaikie | bbafb8a | 2012-03-11 07:00:24 +0000 | [diff] [blame] | 3119 | CFRefReport *report = new CFRefReport(*BT, C.getASTContext().getLangOpts(), | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3120 | C.isObjCGCEnabled(), SummaryLog, | 
|  | 3121 | N, Sym); | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3122 | report->addRange(ErrorRange); | 
|  | 3123 | C.EmitReport(report); | 
|  | 3124 | } | 
|  | 3125 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3126 | //===----------------------------------------------------------------------===// | 
|  | 3127 | // Handle the return values of retain-count-related functions. | 
|  | 3128 | //===----------------------------------------------------------------------===// | 
|  | 3129 |  | 
|  | 3130 | bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 3131 | // Get the callee. We're only interested in simple C functions. | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3132 | ProgramStateRef state = C.getState(); | 
| Anna Zaks | c6aa531 | 2011-12-01 05:57:37 +0000 | [diff] [blame] | 3133 | const FunctionDecl *FD = C.getCalleeDecl(CE); | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 3134 | if (!FD) | 
|  | 3135 | return false; | 
|  | 3136 |  | 
|  | 3137 | IdentifierInfo *II = FD->getIdentifier(); | 
|  | 3138 | if (!II) | 
|  | 3139 | return false; | 
|  | 3140 |  | 
|  | 3141 | // For now, we're only handling the functions that return aliases of their | 
|  | 3142 | // arguments: CFRetain and CFMakeCollectable (and their families). | 
|  | 3143 | // Eventually we should add other functions we can model entirely, | 
|  | 3144 | // such as CFRelease, which don't invalidate their arguments or globals. | 
|  | 3145 | if (CE->getNumArgs() != 1) | 
|  | 3146 | return false; | 
|  | 3147 |  | 
|  | 3148 | // Get the name of the function. | 
|  | 3149 | StringRef FName = II->getName(); | 
|  | 3150 | FName = FName.substr(FName.find_first_not_of('_')); | 
|  | 3151 |  | 
|  | 3152 | // See if it's one of the specific functions we know how to eval. | 
|  | 3153 | bool canEval = false; | 
|  | 3154 |  | 
| Anna Zaks | c6aa531 | 2011-12-01 05:57:37 +0000 | [diff] [blame] | 3155 | QualType ResultTy = CE->getCallReturnType(); | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 3156 | if (ResultTy->isObjCIdType()) { | 
|  | 3157 | // Handle: id NSMakeCollectable(CFTypeRef) | 
|  | 3158 | canEval = II->isStr("NSMakeCollectable"); | 
|  | 3159 | } else if (ResultTy->isPointerType()) { | 
|  | 3160 | // Handle: (CF|CG)Retain | 
|  | 3161 | //         CFMakeCollectable | 
|  | 3162 | // It's okay to be a little sloppy here (CGMakeCollectable doesn't exist). | 
|  | 3163 | if (cocoa::isRefType(ResultTy, "CF", FName) || | 
|  | 3164 | cocoa::isRefType(ResultTy, "CG", FName)) { | 
|  | 3165 | canEval = isRetain(FD, FName) || isMakeCollectable(FD, FName); | 
|  | 3166 | } | 
|  | 3167 | } | 
|  | 3168 |  | 
|  | 3169 | if (!canEval) | 
|  | 3170 | return false; | 
|  | 3171 |  | 
|  | 3172 | // Bind the return value. | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 3173 | const LocationContext *LCtx = C.getLocationContext(); | 
|  | 3174 | SVal RetVal = state->getSVal(CE->getArg(0), LCtx); | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 3175 | if (RetVal.isUnknown()) { | 
|  | 3176 | // If the receiver is unknown, conjure a return value. | 
|  | 3177 | SValBuilder &SVB = C.getSValBuilder(); | 
| Anna Zaks | 23d7ba3 | 2011-10-04 20:43:05 +0000 | [diff] [blame] | 3178 | unsigned Count = C.getCurrentBlockCount(); | 
| Ted Kremenek | d519cae | 2012-02-17 23:13:45 +0000 | [diff] [blame] | 3179 | SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 3180 | } | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 3181 | state = state->BindExpr(CE, LCtx, RetVal, false); | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 3182 |  | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3183 | // FIXME: This should not be necessary, but otherwise the argument seems to be | 
|  | 3184 | // considered alive during the next statement. | 
|  | 3185 | if (const MemRegion *ArgRegion = RetVal.getAsRegion()) { | 
|  | 3186 | // Save the refcount status of the argument. | 
|  | 3187 | SymbolRef Sym = RetVal.getAsLocSymbol(); | 
|  | 3188 | RefBindings::data_type *Binding = 0; | 
|  | 3189 | if (Sym) | 
|  | 3190 | Binding = state->get<RefBindings>(Sym); | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 3191 |  | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3192 | // Invalidate the argument region. | 
| Anna Zaks | 23d7ba3 | 2011-10-04 20:43:05 +0000 | [diff] [blame] | 3193 | unsigned Count = C.getCurrentBlockCount(); | 
| Ted Kremenek | d519cae | 2012-02-17 23:13:45 +0000 | [diff] [blame] | 3194 | state = state->invalidateRegions(ArgRegion, CE, Count, LCtx); | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 3195 |  | 
| Jordy Rose | 5b31d7a | 2011-08-22 23:48:23 +0000 | [diff] [blame] | 3196 | // Restore the refcount status of the argument. | 
|  | 3197 | if (Binding) | 
|  | 3198 | state = state->set<RefBindings>(Sym, *Binding); | 
|  | 3199 | } | 
|  | 3200 |  | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 3201 | C.addTransition(state); | 
| Jordy Rose | 898a148 | 2011-08-21 21:58:18 +0000 | [diff] [blame] | 3202 | return true; | 
|  | 3203 | } | 
|  | 3204 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3205 | //===----------------------------------------------------------------------===// | 
|  | 3206 | // Handle return statements. | 
|  | 3207 | //===----------------------------------------------------------------------===// | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3208 |  | 
| Ted Kremenek | ef31f37 | 2012-02-25 02:09:09 +0000 | [diff] [blame] | 3209 | // Return true if the current LocationContext has no caller context. | 
|  | 3210 | static bool inTopFrame(CheckerContext &C) { | 
|  | 3211 | const LocationContext *LC = C.getLocationContext(); | 
|  | 3212 | return LC->getParent() == 0; | 
|  | 3213 | } | 
|  | 3214 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3215 | void RetainCountChecker::checkPreStmt(const ReturnStmt *S, | 
|  | 3216 | CheckerContext &C) const { | 
| Ted Kremenek | ef31f37 | 2012-02-25 02:09:09 +0000 | [diff] [blame] | 3217 |  | 
|  | 3218 | // Only adjust the reference count if this is the top-level call frame, | 
|  | 3219 | // and not the result of inlining.  In the future, we should do | 
|  | 3220 | // better checking even for inlined calls, and see if they match | 
|  | 3221 | // with their expected semantics (e.g., the method should return a retained | 
|  | 3222 | // object, etc.). | 
|  | 3223 | if (!inTopFrame(C)) | 
|  | 3224 | return; | 
|  | 3225 |  | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3226 | const Expr *RetE = S->getRetValue(); | 
|  | 3227 | if (!RetE) | 
|  | 3228 | return; | 
|  | 3229 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3230 | ProgramStateRef state = C.getState(); | 
| Ted Kremenek | 632e3b7 | 2012-01-06 22:09:28 +0000 | [diff] [blame] | 3231 | SymbolRef Sym = | 
|  | 3232 | state->getSValAsScalarOrLoc(RetE, C.getLocationContext()).getAsLocSymbol(); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3233 | if (!Sym) | 
|  | 3234 | return; | 
|  | 3235 |  | 
|  | 3236 | // Get the reference count binding (if any). | 
|  | 3237 | const RefVal *T = state->get<RefBindings>(Sym); | 
|  | 3238 | if (!T) | 
|  | 3239 | return; | 
|  | 3240 |  | 
|  | 3241 | // Change the reference count. | 
|  | 3242 | RefVal X = *T; | 
|  | 3243 |  | 
|  | 3244 | switch (X.getKind()) { | 
|  | 3245 | case RefVal::Owned: { | 
|  | 3246 | unsigned cnt = X.getCount(); | 
|  | 3247 | assert(cnt > 0); | 
|  | 3248 | X.setCount(cnt - 1); | 
|  | 3249 | X = X ^ RefVal::ReturnedOwned; | 
|  | 3250 | break; | 
|  | 3251 | } | 
|  | 3252 |  | 
|  | 3253 | case RefVal::NotOwned: { | 
|  | 3254 | unsigned cnt = X.getCount(); | 
|  | 3255 | if (cnt) { | 
|  | 3256 | X.setCount(cnt - 1); | 
|  | 3257 | X = X ^ RefVal::ReturnedOwned; | 
|  | 3258 | } | 
|  | 3259 | else { | 
|  | 3260 | X = X ^ RefVal::ReturnedNotOwned; | 
|  | 3261 | } | 
|  | 3262 | break; | 
|  | 3263 | } | 
|  | 3264 |  | 
|  | 3265 | default: | 
|  | 3266 | return; | 
|  | 3267 | } | 
|  | 3268 |  | 
|  | 3269 | // Update the binding. | 
|  | 3270 | state = state->set<RefBindings>(Sym, X); | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 3271 | ExplodedNode *Pred = C.addTransition(state); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3272 |  | 
|  | 3273 | // At this point we have updated the state properly. | 
|  | 3274 | // Everything after this is merely checking to see if the return value has | 
|  | 3275 | // been over- or under-retained. | 
|  | 3276 |  | 
|  | 3277 | // Did we cache out? | 
|  | 3278 | if (!Pred) | 
|  | 3279 | return; | 
|  | 3280 |  | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3281 | // Update the autorelease counts. | 
|  | 3282 | static SimpleProgramPointTag | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3283 | AutoreleaseTag("RetainCountChecker : Autorelease"); | 
| Anna Zaks | c4aa22c | 2011-10-05 23:44:11 +0000 | [diff] [blame] | 3284 | GenericNodeBuilderRefCount Bd(C, &AutoreleaseTag); | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3285 | llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, C, Sym, X); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3286 |  | 
|  | 3287 | // Did we cache out? | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3288 | if (!Pred) | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3289 | return; | 
|  | 3290 |  | 
|  | 3291 | // Get the updated binding. | 
|  | 3292 | T = state->get<RefBindings>(Sym); | 
|  | 3293 | assert(T); | 
|  | 3294 | X = *T; | 
|  | 3295 |  | 
|  | 3296 | // Consult the summary of the enclosing method. | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3297 | RetainSummaryManager &Summaries = getSummaryManager(C); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3298 | const Decl *CD = &Pred->getCodeDecl(); | 
|  | 3299 |  | 
|  | 3300 | if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) { | 
|  | 3301 | // Unlike regular functions, /all/ ObjC methods are assumed to always | 
|  | 3302 | // follow Cocoa retain-count conventions, not just those with special | 
|  | 3303 | // names or attributes. | 
| Jordy Rose | 8b289a2 | 2011-08-25 00:10:37 +0000 | [diff] [blame] | 3304 | const RetainSummary *Summ = Summaries.getMethodSummary(MD); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3305 | RetEffect RE = Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet(); | 
|  | 3306 | checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state); | 
|  | 3307 | } | 
|  | 3308 |  | 
|  | 3309 | if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) { | 
|  | 3310 | if (!isa<CXXMethodDecl>(FD)) | 
| Anna Zaks | f4c5ea5 | 2012-05-04 22:18:39 +0000 | [diff] [blame] | 3311 | if (const RetainSummary *Summ = Summaries.getSummary(FD, 0)) | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3312 | checkReturnWithRetEffect(S, C, Pred, Summ->getRetEffect(), X, | 
|  | 3313 | Sym, state); | 
|  | 3314 | } | 
|  | 3315 | } | 
|  | 3316 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3317 | void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, | 
|  | 3318 | CheckerContext &C, | 
|  | 3319 | ExplodedNode *Pred, | 
|  | 3320 | RetEffect RE, RefVal X, | 
|  | 3321 | SymbolRef Sym, | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3322 | ProgramStateRef state) const { | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3323 | // Any leaks or other errors? | 
|  | 3324 | if (X.isReturnedOwned() && X.getCount() == 0) { | 
|  | 3325 | if (RE.getKind() != RetEffect::NoRet) { | 
|  | 3326 | bool hasError = false; | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3327 | if (C.isObjCGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3328 | // Things are more complicated with garbage collection.  If the | 
|  | 3329 | // returned object is suppose to be an Objective-C object, we have | 
|  | 3330 | // a leak (as the caller expects a GC'ed object) because no | 
|  | 3331 | // method should return ownership unless it returns a CF object. | 
|  | 3332 | hasError = true; | 
|  | 3333 | X = X ^ RefVal::ErrorGCLeakReturned; | 
|  | 3334 | } | 
|  | 3335 | else if (!RE.isOwned()) { | 
|  | 3336 | // Either we are using GC and the returned object is a CF type | 
|  | 3337 | // or we aren't using GC.  In either case, we expect that the | 
|  | 3338 | // enclosing method is expected to return ownership. | 
|  | 3339 | hasError = true; | 
|  | 3340 | X = X ^ RefVal::ErrorLeakReturned; | 
|  | 3341 | } | 
|  | 3342 |  | 
|  | 3343 | if (hasError) { | 
|  | 3344 | // Generate an error node. | 
|  | 3345 | state = state->set<RefBindings>(Sym, X); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3346 |  | 
|  | 3347 | static SimpleProgramPointTag | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3348 | ReturnOwnLeakTag("RetainCountChecker : ReturnsOwnLeak"); | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 3349 | ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3350 | if (N) { | 
| David Blaikie | bbafb8a | 2012-03-11 07:00:24 +0000 | [diff] [blame] | 3351 | const LangOptions &LOpts = C.getASTContext().getLangOpts(); | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3352 | bool GCEnabled = C.isObjCGCEnabled(); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3353 | CFRefReport *report = | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3354 | new CFRefLeakReport(*getLeakAtReturnBug(LOpts, GCEnabled), | 
|  | 3355 | LOpts, GCEnabled, SummaryLog, | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3356 | N, Sym, C); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3357 | C.EmitReport(report); | 
|  | 3358 | } | 
|  | 3359 | } | 
|  | 3360 | } | 
|  | 3361 | } else if (X.isReturnedNotOwned()) { | 
|  | 3362 | if (RE.isOwned()) { | 
|  | 3363 | // Trying to return a not owned object to a caller expecting an | 
|  | 3364 | // owned object. | 
|  | 3365 | state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3366 |  | 
|  | 3367 | static SimpleProgramPointTag | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3368 | ReturnNotOwnedTag("RetainCountChecker : ReturnNotOwnedForOwned"); | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 3369 | ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3370 | if (N) { | 
| Jordy Rose | 4ba0ba4 | 2011-08-25 00:34:03 +0000 | [diff] [blame] | 3371 | if (!returnNotOwnedForOwned) | 
|  | 3372 | returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned()); | 
|  | 3373 |  | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3374 | CFRefReport *report = | 
| Jordy Rose | 4ba0ba4 | 2011-08-25 00:34:03 +0000 | [diff] [blame] | 3375 | new CFRefReport(*returnNotOwnedForOwned, | 
| David Blaikie | bbafb8a | 2012-03-11 07:00:24 +0000 | [diff] [blame] | 3376 | C.getASTContext().getLangOpts(), | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3377 | C.isObjCGCEnabled(), SummaryLog, N, Sym); | 
| Jordy Rose | 298cc4d | 2011-08-23 19:43:16 +0000 | [diff] [blame] | 3378 | C.EmitReport(report); | 
|  | 3379 | } | 
|  | 3380 | } | 
|  | 3381 | } | 
|  | 3382 | } | 
|  | 3383 |  | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3384 | //===----------------------------------------------------------------------===// | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3385 | // Check various ways a symbol can be invalidated. | 
|  | 3386 | //===----------------------------------------------------------------------===// | 
|  | 3387 |  | 
| Anna Zaks | 3e0f415 | 2011-10-06 00:43:15 +0000 | [diff] [blame] | 3388 | void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3389 | CheckerContext &C) const { | 
|  | 3390 | // Are we storing to something that causes the value to "escape"? | 
|  | 3391 | bool escapes = true; | 
|  | 3392 |  | 
|  | 3393 | // A value escapes in three possible cases (this may change): | 
|  | 3394 | // | 
|  | 3395 | // (1) we are binding to something that is not a memory region. | 
|  | 3396 | // (2) we are binding to a memregion that does not have stack storage | 
|  | 3397 | // (3) we are binding to a memregion with stack storage that the store | 
|  | 3398 | //     does not understand. | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3399 | ProgramStateRef state = C.getState(); | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3400 |  | 
|  | 3401 | if (loc::MemRegionVal *regionLoc = dyn_cast<loc::MemRegionVal>(&loc)) { | 
|  | 3402 | escapes = !regionLoc->getRegion()->hasStackStorage(); | 
|  | 3403 |  | 
|  | 3404 | if (!escapes) { | 
|  | 3405 | // To test (3), generate a new state with the binding added.  If it is | 
|  | 3406 | // the same state, then it escapes (since the store cannot represent | 
|  | 3407 | // the binding). | 
| Anna Zaks | 70de772 | 2012-05-02 00:15:40 +0000 | [diff] [blame] | 3408 | // Do this only if we know that the store is not supposed to generate the | 
|  | 3409 | // same state. | 
|  | 3410 | SVal StoredVal = state->getSVal(regionLoc->getRegion()); | 
|  | 3411 | if (StoredVal != val) | 
|  | 3412 | escapes = (state == (state->bindLoc(*regionLoc, val))); | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3413 | } | 
| Ted Kremenek | e9a5bcf | 2012-03-27 01:12:45 +0000 | [diff] [blame] | 3414 | if (!escapes) { | 
|  | 3415 | // Case 4: We do not currently model what happens when a symbol is | 
|  | 3416 | // assigned to a struct field, so be conservative here and let the symbol | 
|  | 3417 | // go. TODO: This could definitely be improved upon. | 
|  | 3418 | escapes = !isa<VarRegion>(regionLoc->getRegion()); | 
|  | 3419 | } | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3420 | } | 
|  | 3421 |  | 
|  | 3422 | // If our store can represent the binding and we aren't storing to something | 
|  | 3423 | // that doesn't have local storage then just return and have the simulation | 
|  | 3424 | // state continue as is. | 
|  | 3425 | if (!escapes) | 
|  | 3426 | return; | 
|  | 3427 |  | 
|  | 3428 | // Otherwise, find all symbols referenced by 'val' that we are tracking | 
|  | 3429 | // and stop tracking them. | 
|  | 3430 | state = state->scanReachableSymbols<StopTrackingCallback>(val).getState(); | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 3431 | C.addTransition(state); | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3432 | } | 
|  | 3433 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3434 | ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3435 | SVal Cond, | 
|  | 3436 | bool Assumption) const { | 
|  | 3437 |  | 
|  | 3438 | // FIXME: We may add to the interface of evalAssume the list of symbols | 
|  | 3439 | //  whose assumptions have changed.  For now we just iterate through the | 
|  | 3440 | //  bindings and check if any of the tracked symbols are NULL.  This isn't | 
|  | 3441 | //  too bad since the number of symbols we will track in practice are | 
|  | 3442 | //  probably small and evalAssume is only called at branches and a few | 
|  | 3443 | //  other places. | 
|  | 3444 | RefBindings B = state->get<RefBindings>(); | 
|  | 3445 |  | 
|  | 3446 | if (B.isEmpty()) | 
|  | 3447 | return state; | 
|  | 3448 |  | 
|  | 3449 | bool changed = false; | 
|  | 3450 | RefBindings::Factory &RefBFactory = state->get_context<RefBindings>(); | 
|  | 3451 |  | 
|  | 3452 | for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { | 
|  | 3453 | // Check if the symbol is null (or equal to any constant). | 
|  | 3454 | // If this is the case, stop tracking the symbol. | 
|  | 3455 | if (state->getSymVal(I.getKey())) { | 
|  | 3456 | changed = true; | 
|  | 3457 | B = RefBFactory.remove(B, I.getKey()); | 
|  | 3458 | } | 
|  | 3459 | } | 
|  | 3460 |  | 
|  | 3461 | if (changed) | 
|  | 3462 | state = state->set<RefBindings>(B); | 
|  | 3463 |  | 
|  | 3464 | return state; | 
|  | 3465 | } | 
|  | 3466 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3467 | ProgramStateRef | 
|  | 3468 | RetainCountChecker::checkRegionChanges(ProgramStateRef state, | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3469 | const StoreManager::InvalidatedSymbols *invalidated, | 
|  | 3470 | ArrayRef<const MemRegion *> ExplicitRegions, | 
| Anna Zaks | 3d34834 | 2012-02-14 21:55:24 +0000 | [diff] [blame] | 3471 | ArrayRef<const MemRegion *> Regions, | 
|  | 3472 | const CallOrObjCMessage *Call) const { | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3473 | if (!invalidated) | 
|  | 3474 | return state; | 
|  | 3475 |  | 
|  | 3476 | llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; | 
|  | 3477 | for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), | 
|  | 3478 | E = ExplicitRegions.end(); I != E; ++I) { | 
|  | 3479 | if (const SymbolicRegion *SR = (*I)->StripCasts()->getAs<SymbolicRegion>()) | 
|  | 3480 | WhitelistedSymbols.insert(SR->getSymbol()); | 
|  | 3481 | } | 
|  | 3482 |  | 
|  | 3483 | for (StoreManager::InvalidatedSymbols::const_iterator I=invalidated->begin(), | 
|  | 3484 | E = invalidated->end(); I!=E; ++I) { | 
|  | 3485 | SymbolRef sym = *I; | 
|  | 3486 | if (WhitelistedSymbols.count(sym)) | 
|  | 3487 | continue; | 
|  | 3488 | // Remove any existing reference-count binding. | 
|  | 3489 | state = state->remove<RefBindings>(sym); | 
|  | 3490 | } | 
|  | 3491 | return state; | 
|  | 3492 | } | 
|  | 3493 |  | 
|  | 3494 | //===----------------------------------------------------------------------===// | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3495 | // Handle dead symbols and end-of-path. | 
|  | 3496 | //===----------------------------------------------------------------------===// | 
|  | 3497 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3498 | std::pair<ExplodedNode *, ProgramStateRef > | 
|  | 3499 | RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3500 | GenericNodeBuilderRefCount Bd, | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3501 | ExplodedNode *Pred, | 
|  | 3502 | CheckerContext &Ctx, | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3503 | SymbolRef Sym, RefVal V) const { | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3504 | unsigned ACnt = V.getAutoreleaseCount(); | 
|  | 3505 |  | 
|  | 3506 | // No autorelease counts?  Nothing to be done. | 
|  | 3507 | if (!ACnt) | 
|  | 3508 | return std::make_pair(Pred, state); | 
|  | 3509 |  | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3510 | assert(!Ctx.isObjCGCEnabled() && "Autorelease counts in GC mode?"); | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3511 | unsigned Cnt = V.getCount(); | 
|  | 3512 |  | 
|  | 3513 | // FIXME: Handle sending 'autorelease' to already released object. | 
|  | 3514 |  | 
|  | 3515 | if (V.getKind() == RefVal::ReturnedOwned) | 
|  | 3516 | ++Cnt; | 
|  | 3517 |  | 
|  | 3518 | if (ACnt <= Cnt) { | 
|  | 3519 | if (ACnt == Cnt) { | 
|  | 3520 | V.clearCounts(); | 
|  | 3521 | if (V.getKind() == RefVal::ReturnedOwned) | 
|  | 3522 | V = V ^ RefVal::ReturnedNotOwned; | 
|  | 3523 | else | 
|  | 3524 | V = V ^ RefVal::NotOwned; | 
|  | 3525 | } else { | 
|  | 3526 | V.setCount(Cnt - ACnt); | 
|  | 3527 | V.setAutoreleaseCount(0); | 
|  | 3528 | } | 
|  | 3529 | state = state->set<RefBindings>(Sym, V); | 
|  | 3530 | ExplodedNode *N = Bd.MakeNode(state, Pred); | 
|  | 3531 | if (N == 0) | 
|  | 3532 | state = 0; | 
|  | 3533 | return std::make_pair(N, state); | 
|  | 3534 | } | 
|  | 3535 |  | 
|  | 3536 | // Woah!  More autorelease counts then retain counts left. | 
|  | 3537 | // Emit hard error. | 
|  | 3538 | V = V ^ RefVal::ErrorOverAutorelease; | 
|  | 3539 | state = state->set<RefBindings>(Sym, V); | 
|  | 3540 |  | 
| Anna Zaks | fc0189a | 2011-10-18 23:05:58 +0000 | [diff] [blame] | 3541 | if (ExplodedNode *N = Bd.MakeNode(state, Pred, true)) { | 
| Dylan Noblesmith | 2c1dd27 | 2012-02-05 02:13:05 +0000 | [diff] [blame] | 3542 | SmallString<128> sbuf; | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3543 | llvm::raw_svector_ostream os(sbuf); | 
|  | 3544 | os << "Object over-autoreleased: object was sent -autorelease "; | 
|  | 3545 | if (V.getAutoreleaseCount() > 1) | 
|  | 3546 | os << V.getAutoreleaseCount() << " times "; | 
|  | 3547 | os << "but the object has a +" << V.getCount() << " retain count"; | 
|  | 3548 |  | 
| Jordy Rose | 4ba0ba4 | 2011-08-25 00:34:03 +0000 | [diff] [blame] | 3549 | if (!overAutorelease) | 
|  | 3550 | overAutorelease.reset(new OverAutorelease()); | 
|  | 3551 |  | 
| David Blaikie | bbafb8a | 2012-03-11 07:00:24 +0000 | [diff] [blame] | 3552 | const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3553 | CFRefReport *report = | 
| Jordy Rose | 4ba0ba4 | 2011-08-25 00:34:03 +0000 | [diff] [blame] | 3554 | new CFRefReport(*overAutorelease, LOpts, /* GCEnabled = */ false, | 
|  | 3555 | SummaryLog, N, Sym, os.str()); | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3556 | Ctx.EmitReport(report); | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3557 | } | 
|  | 3558 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3559 | return std::make_pair((ExplodedNode *)0, (ProgramStateRef )0); | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3560 | } | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3561 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3562 | ProgramStateRef | 
|  | 3563 | RetainCountChecker::handleSymbolDeath(ProgramStateRef state, | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3564 | SymbolRef sid, RefVal V, | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3565 | SmallVectorImpl<SymbolRef> &Leaked) const { | 
| Jordy Rose | 03a8f9e | 2011-08-24 04:48:19 +0000 | [diff] [blame] | 3566 | bool hasLeak = false; | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3567 | if (V.isOwned()) | 
|  | 3568 | hasLeak = true; | 
|  | 3569 | else if (V.isNotOwned() || V.isReturnedOwned()) | 
|  | 3570 | hasLeak = (V.getCount() > 0); | 
|  | 3571 |  | 
|  | 3572 | if (!hasLeak) | 
|  | 3573 | return state->remove<RefBindings>(sid); | 
|  | 3574 |  | 
|  | 3575 | Leaked.push_back(sid); | 
|  | 3576 | return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak); | 
|  | 3577 | } | 
|  | 3578 |  | 
|  | 3579 | ExplodedNode * | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3580 | RetainCountChecker::processLeaks(ProgramStateRef state, | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3581 | SmallVectorImpl<SymbolRef> &Leaked, | 
|  | 3582 | GenericNodeBuilderRefCount &Builder, | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3583 | CheckerContext &Ctx, | 
|  | 3584 | ExplodedNode *Pred) const { | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3585 | if (Leaked.empty()) | 
|  | 3586 | return Pred; | 
|  | 3587 |  | 
|  | 3588 | // Generate an intermediate node representing the leak point. | 
|  | 3589 | ExplodedNode *N = Builder.MakeNode(state, Pred); | 
|  | 3590 |  | 
|  | 3591 | if (N) { | 
|  | 3592 | for (SmallVectorImpl<SymbolRef>::iterator | 
|  | 3593 | I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { | 
|  | 3594 |  | 
| David Blaikie | bbafb8a | 2012-03-11 07:00:24 +0000 | [diff] [blame] | 3595 | const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3596 | bool GCEnabled = Ctx.isObjCGCEnabled(); | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3597 | CFRefBug *BT = Pred ? getLeakWithinFunctionBug(LOpts, GCEnabled) | 
|  | 3598 | : getLeakAtReturnBug(LOpts, GCEnabled); | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3599 | assert(BT && "BugType not initialized."); | 
| Jordy Rose | 184bd14 | 2011-08-24 22:39:09 +0000 | [diff] [blame] | 3600 |  | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3601 | CFRefLeakReport *report = new CFRefLeakReport(*BT, LOpts, GCEnabled, | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3602 | SummaryLog, N, *I, Ctx); | 
|  | 3603 | Ctx.EmitReport(report); | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3604 | } | 
|  | 3605 | } | 
|  | 3606 |  | 
|  | 3607 | return N; | 
|  | 3608 | } | 
|  | 3609 |  | 
| Anna Zaks | 3eae334 | 2011-10-25 19:56:48 +0000 | [diff] [blame] | 3610 | void RetainCountChecker::checkEndPath(CheckerContext &Ctx) const { | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3611 | ProgramStateRef state = Ctx.getState(); | 
| Anna Zaks | 3eae334 | 2011-10-25 19:56:48 +0000 | [diff] [blame] | 3612 | GenericNodeBuilderRefCount Bd(Ctx); | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3613 | RefBindings B = state->get<RefBindings>(); | 
| Anna Zaks | 3eae334 | 2011-10-25 19:56:48 +0000 | [diff] [blame] | 3614 | ExplodedNode *Pred = Ctx.getPredecessor(); | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3615 |  | 
|  | 3616 | for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3617 | llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, Ctx, | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3618 | I->first, I->second); | 
|  | 3619 | if (!state) | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3620 | return; | 
|  | 3621 | } | 
|  | 3622 |  | 
| Ted Kremenek | a2bbac3 | 2012-02-07 00:24:33 +0000 | [diff] [blame] | 3623 | // If the current LocationContext has a parent, don't check for leaks. | 
|  | 3624 | // We will do that later. | 
|  | 3625 | // FIXME: we should instead check for imblances of the retain/releases, | 
|  | 3626 | // and suggest annotations. | 
|  | 3627 | if (Ctx.getLocationContext()->getParent()) | 
|  | 3628 | return; | 
|  | 3629 |  | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3630 | B = state->get<RefBindings>(); | 
|  | 3631 | SmallVector<SymbolRef, 10> Leaked; | 
|  | 3632 |  | 
|  | 3633 | for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3634 | state = handleSymbolDeath(state, I->first, I->second, Leaked); | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3635 |  | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3636 | processLeaks(state, Leaked, Bd, Ctx, Pred); | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3637 | } | 
|  | 3638 |  | 
|  | 3639 | const ProgramPointTag * | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3640 | RetainCountChecker::getDeadSymbolTag(SymbolRef sym) const { | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3641 | const SimpleProgramPointTag *&tag = DeadSymbolTags[sym]; | 
|  | 3642 | if (!tag) { | 
| Dylan Noblesmith | 2c1dd27 | 2012-02-05 02:13:05 +0000 | [diff] [blame] | 3643 | SmallString<64> buf; | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3644 | llvm::raw_svector_ostream out(buf); | 
| Anna Zaks | 2235165 | 2011-12-05 18:58:11 +0000 | [diff] [blame] | 3645 | out << "RetainCountChecker : Dead Symbol : "; | 
|  | 3646 | sym->dumpToStream(out); | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3647 | tag = new SimpleProgramPointTag(out.str()); | 
|  | 3648 | } | 
|  | 3649 | return tag; | 
|  | 3650 | } | 
|  | 3651 |  | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3652 | void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, | 
|  | 3653 | CheckerContext &C) const { | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3654 | ExplodedNode *Pred = C.getPredecessor(); | 
|  | 3655 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3656 | ProgramStateRef state = C.getState(); | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3657 | RefBindings B = state->get<RefBindings>(); | 
|  | 3658 |  | 
|  | 3659 | // Update counts from autorelease pools | 
|  | 3660 | for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), | 
|  | 3661 | E = SymReaper.dead_end(); I != E; ++I) { | 
|  | 3662 | SymbolRef Sym = *I; | 
|  | 3663 | if (const RefVal *T = B.lookup(Sym)){ | 
|  | 3664 | // Use the symbol as the tag. | 
|  | 3665 | // FIXME: This might not be as unique as we would like. | 
| Anna Zaks | c4aa22c | 2011-10-05 23:44:11 +0000 | [diff] [blame] | 3666 | GenericNodeBuilderRefCount Bd(C, getDeadSymbolTag(Sym)); | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3667 | llvm::tie(Pred, state) = handleAutoreleaseCounts(state, Bd, Pred, C, | 
| Jordy Rose | 6763e38 | 2011-08-23 20:07:14 +0000 | [diff] [blame] | 3668 | Sym, *T); | 
|  | 3669 | if (!state) | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3670 | return; | 
|  | 3671 | } | 
|  | 3672 | } | 
|  | 3673 |  | 
|  | 3674 | B = state->get<RefBindings>(); | 
|  | 3675 | SmallVector<SymbolRef, 10> Leaked; | 
|  | 3676 |  | 
|  | 3677 | for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), | 
|  | 3678 | E = SymReaper.dead_end(); I != E; ++I) { | 
|  | 3679 | if (const RefVal *T = B.lookup(*I)) | 
|  | 3680 | state = handleSymbolDeath(state, *I, *T, Leaked); | 
|  | 3681 | } | 
|  | 3682 |  | 
|  | 3683 | { | 
| Anna Zaks | c4aa22c | 2011-10-05 23:44:11 +0000 | [diff] [blame] | 3684 | GenericNodeBuilderRefCount Bd(C, this); | 
| Anna Zaks | 58734db | 2011-10-25 19:57:11 +0000 | [diff] [blame] | 3685 | Pred = processLeaks(state, Leaked, Bd, C, Pred); | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3686 | } | 
|  | 3687 |  | 
|  | 3688 | // Did we cache out? | 
|  | 3689 | if (!Pred) | 
|  | 3690 | return; | 
|  | 3691 |  | 
|  | 3692 | // Now generate a new node that nukes the old bindings. | 
|  | 3693 | RefBindings::Factory &F = state->get_context<RefBindings>(); | 
|  | 3694 |  | 
|  | 3695 | for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), | 
|  | 3696 | E = SymReaper.dead_end(); I != E; ++I) | 
|  | 3697 | B = F.remove(B, *I); | 
|  | 3698 |  | 
|  | 3699 | state = state->set<RefBindings>(B); | 
| Anna Zaks | da4c8d6 | 2011-10-26 21:06:34 +0000 | [diff] [blame] | 3700 | C.addTransition(state, Pred); | 
| Jordy Rose | 7861276 | 2011-08-23 19:01:07 +0000 | [diff] [blame] | 3701 | } | 
|  | 3702 |  | 
| Ted Kremenek | 70a8788 | 2009-11-25 22:17:44 +0000 | [diff] [blame] | 3703 | //===----------------------------------------------------------------------===// | 
| Jordy Rose | 58a20d3 | 2011-08-28 19:11:56 +0000 | [diff] [blame] | 3704 | // Debug printing of refcount bindings and autorelease pools. | 
|  | 3705 | //===----------------------------------------------------------------------===// | 
|  | 3706 |  | 
|  | 3707 | static void PrintPool(raw_ostream &Out, SymbolRef Sym, | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3708 | ProgramStateRef State) { | 
| Jordy Rose | 58a20d3 | 2011-08-28 19:11:56 +0000 | [diff] [blame] | 3709 | Out << ' '; | 
|  | 3710 | if (Sym) | 
| Anna Zaks | 2235165 | 2011-12-05 18:58:11 +0000 | [diff] [blame] | 3711 | Sym->dumpToStream(Out); | 
| Jordy Rose | 58a20d3 | 2011-08-28 19:11:56 +0000 | [diff] [blame] | 3712 | else | 
|  | 3713 | Out << "<pool>"; | 
|  | 3714 | Out << ":{"; | 
|  | 3715 |  | 
|  | 3716 | // Get the contents of the pool. | 
|  | 3717 | if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym)) | 
|  | 3718 | for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I) | 
|  | 3719 | Out << '(' << I.getKey() << ',' << I.getData() << ')'; | 
|  | 3720 |  | 
|  | 3721 | Out << '}'; | 
|  | 3722 | } | 
|  | 3723 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3724 | static bool UsesAutorelease(ProgramStateRef state) { | 
| Jordy Rose | 58a20d3 | 2011-08-28 19:11:56 +0000 | [diff] [blame] | 3725 | // A state uses autorelease if it allocated an autorelease pool or if it has | 
|  | 3726 | // objects in the caller's autorelease pool. | 
|  | 3727 | return !state->get<AutoreleaseStack>().isEmpty() || | 
|  | 3728 | state->get<AutoreleasePoolContents>(SymbolRef()); | 
|  | 3729 | } | 
|  | 3730 |  | 
| Ted Kremenek | 49b1e38 | 2012-01-26 21:29:00 +0000 | [diff] [blame] | 3731 | void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3732 | const char *NL, const char *Sep) const { | 
| Jordy Rose | 58a20d3 | 2011-08-28 19:11:56 +0000 | [diff] [blame] | 3733 |  | 
|  | 3734 | RefBindings B = State->get<RefBindings>(); | 
|  | 3735 |  | 
|  | 3736 | if (!B.isEmpty()) | 
|  | 3737 | Out << Sep << NL; | 
|  | 3738 |  | 
|  | 3739 | for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { | 
|  | 3740 | Out << I->first << " : "; | 
|  | 3741 | I->second.print(Out); | 
|  | 3742 | Out << NL; | 
|  | 3743 | } | 
|  | 3744 |  | 
|  | 3745 | // Print the autorelease stack. | 
|  | 3746 | if (UsesAutorelease(State)) { | 
|  | 3747 | Out << Sep << NL << "AR pool stack:"; | 
|  | 3748 | ARStack Stack = State->get<AutoreleaseStack>(); | 
|  | 3749 |  | 
|  | 3750 | PrintPool(Out, SymbolRef(), State);  // Print the caller's pool. | 
|  | 3751 | for (ARStack::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I) | 
|  | 3752 | PrintPool(Out, *I, State); | 
|  | 3753 |  | 
|  | 3754 | Out << NL; | 
|  | 3755 | } | 
|  | 3756 | } | 
|  | 3757 |  | 
|  | 3758 | //===----------------------------------------------------------------------===// | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3759 | // Checker registration. | 
| Ted Kremenek | 819e9b6 | 2008-03-11 06:39:11 +0000 | [diff] [blame] | 3760 | //===----------------------------------------------------------------------===// | 
|  | 3761 |  | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3762 | void ento::registerRetainCountChecker(CheckerManager &Mgr) { | 
| Jordy Rose | 75e680e | 2011-09-02 06:44:22 +0000 | [diff] [blame] | 3763 | Mgr.registerChecker<RetainCountChecker>(); | 
| Jordy Rose | c49ec53 | 2011-09-02 05:55:19 +0000 | [diff] [blame] | 3764 | } | 
|  | 3765 |  |