1 //== ObjCSelfInitChecker.cpp - Checker for 'self' initialization -*- C++ -*--=//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This defines ObjCSelfInitChecker, a builtin check that checks for uses of
11 // 'self' before proper initialization.
13 //===----------------------------------------------------------------------===//
15 // This checks initialization methods to verify that they assign 'self' to the
16 // result of an initialization call (e.g. [super init], or [self initWith..])
17 // before using 'self' or any instance variable.
19 // To perform the required checking, values are tagged wih flags that indicate
20 // 1) if the object is the one pointed to by 'self', and 2) if the object
21 // is the result of an initializer (e.g. [super init]).
23 // Uses of an object that is true for 1) but not 2) trigger a diagnostic.
24 // The uses that are currently checked are:
25 // - Using instance variables.
26 // - Returning the object.
28 // Note that we don't check for an invalid 'self' that is the receiver of an
29 // obj-c message expression to cut down false positives where logging functions
30 // get information from self (like its class) or doing "invalidation" on self
31 // when the initialization fails.
33 // Because the object that 'self' points to gets invalidated when a call
34 // receives a reference to 'self', the checker keeps track and passes the flags
35 // for 1) and 2) to the new object that 'self' points to after the call.
37 // FIXME (rdar://7937506): In the case of:
40 // Have an extra PathDiagnosticPiece in the path that says "called [super init],
41 // but didn't assign the result to self."
43 //===----------------------------------------------------------------------===//
45 // FIXME: Somehow stick the link to Apple's documentation about initializing
46 // objects in the diagnostics.
47 // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html
49 #include "ClangSACheckers.h"
50 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
51 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h"
52 #include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
53 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
54 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
55 #include "clang/AST/ParentMap.h"
57 using namespace clang
;
60 static bool shouldRunOnFunctionOrMethod(const NamedDecl
*ND
);
61 static bool isInitializationMethod(const ObjCMethodDecl
*MD
);
62 static bool isInitMessage(const ObjCMessage
&msg
);
63 static bool isSelfVar(SVal location
, CheckerContext
&C
);
67 /// \brief No flag set.
69 /// \brief Value came from 'self'.
71 /// \brief Value came from the result of an initializer (e.g. [super init]).
72 SelfFlag_InitRes
= 0x2
77 class ObjCSelfInitChecker
: public CheckerVisitor
<ObjCSelfInitChecker
> {
78 /// \brief A call receiving a reference to 'self' invalidates the object that
79 /// 'self' contains. This field keeps the "self flags" assigned to the 'self'
80 /// object before the call and assign them to the new object that 'self'
81 /// points to after the call.
82 SelfFlagEnum preCallSelfFlags
;
85 static void *getTag() { static int tag
= 0; return &tag
; }
86 void postVisitObjCMessage(CheckerContext
&C
, ObjCMessage msg
);
87 void PostVisitObjCIvarRefExpr(CheckerContext
&C
, const ObjCIvarRefExpr
*E
);
88 void PreVisitReturnStmt(CheckerContext
&C
, const ReturnStmt
*S
);
89 void PreVisitGenericCall(CheckerContext
&C
, const CallExpr
*CE
);
90 void PostVisitGenericCall(CheckerContext
&C
, const CallExpr
*CE
);
91 virtual void visitLocation(CheckerContext
&C
, const Stmt
*S
, SVal location
,
94 } // end anonymous namespace
96 static void RegisterObjCSelfInitChecker(ExprEngine
&Eng
) {
97 if (Eng
.getContext().getLangOptions().ObjC1
)
98 Eng
.registerCheck(new ObjCSelfInitChecker());
101 void ento::registerObjCSelfInitChecker(CheckerManager
&mgr
) {
102 mgr
.addCheckerRegisterFunction(RegisterObjCSelfInitChecker
);
107 class InitSelfBug
: public BugType
{
108 const std::string desc
;
110 InitSelfBug() : BugType("missing \"self = [(super or self) init...]\"",
111 "missing \"self = [(super or self) init...]\"") {}
114 } // end anonymous namespace
116 typedef llvm::ImmutableMap
<SymbolRef
, unsigned> SelfFlag
;
117 namespace { struct CalledInit
{}; }
122 struct GRStateTrait
<SelfFlag
> : public GRStatePartialTrait
<SelfFlag
> {
123 static void* GDMIndex() {
124 static int index
= 0;
129 struct GRStateTrait
<CalledInit
> : public GRStatePartialTrait
<bool> {
130 static void *GDMIndex() { static int index
= 0; return &index
; }
135 static SelfFlagEnum
getSelfFlags(SVal val
, const GRState
*state
) {
136 if (SymbolRef sym
= val
.getAsSymbol())
137 if (const unsigned *attachedFlags
= state
->get
<SelfFlag
>(sym
))
138 return (SelfFlagEnum
)*attachedFlags
;
139 return SelfFlag_None
;
142 static SelfFlagEnum
getSelfFlags(SVal val
, CheckerContext
&C
) {
143 return getSelfFlags(val
, C
.getState());
146 static void addSelfFlag(const GRState
*state
, SVal val
,
147 SelfFlagEnum flag
, CheckerContext
&C
) {
148 // We tag the symbol that the SVal wraps.
149 if (SymbolRef sym
= val
.getAsSymbol())
150 C
.addTransition(state
->set
<SelfFlag
>(sym
, getSelfFlags(val
, C
) | flag
));
153 static bool hasSelfFlag(SVal val
, SelfFlagEnum flag
, CheckerContext
&C
) {
154 return getSelfFlags(val
, C
) & flag
;
157 /// \brief Returns true of the value of the expression is the object that 'self'
158 /// points to and is an object that did not come from the result of calling
160 static bool isInvalidSelf(const Expr
*E
, CheckerContext
&C
) {
161 SVal exprVal
= C
.getState()->getSVal(E
);
162 if (!hasSelfFlag(exprVal
, SelfFlag_Self
, C
))
163 return false; // value did not come from 'self'.
164 if (hasSelfFlag(exprVal
, SelfFlag_InitRes
, C
))
165 return false; // 'self' is properly initialized.
170 static void checkForInvalidSelf(const Expr
*E
, CheckerContext
&C
,
171 const char *errorStr
) {
175 if (!C
.getState()->get
<CalledInit
>())
178 if (!isInvalidSelf(E
, C
))
181 // Generate an error node.
182 ExplodedNode
*N
= C
.generateSink();
186 EnhancedBugReport
*report
=
187 new EnhancedBugReport(*new InitSelfBug(), errorStr
, N
);
188 C
.EmitReport(report
);
191 void ObjCSelfInitChecker::postVisitObjCMessage(CheckerContext
&C
,
193 // When encountering a message that does initialization (init rule),
194 // tag the return value so that we know later on that if self has this value
195 // then it is properly initialized.
197 // FIXME: A callback should disable checkers at the start of functions.
198 if (!shouldRunOnFunctionOrMethod(dyn_cast
<NamedDecl
>(
199 C
.getCurrentAnalysisContext()->getDecl())))
202 if (isInitMessage(msg
)) {
203 // Tag the return value as the result of an initializer.
204 const GRState
*state
= C
.getState();
206 // FIXME this really should be context sensitive, where we record
207 // the current stack frame (for IPA). Also, we need to clean this
208 // value out when we return from this method.
209 state
= state
->set
<CalledInit
>(true);
211 SVal V
= state
->getSVal(msg
.getOriginExpr());
212 addSelfFlag(state
, V
, SelfFlag_InitRes
, C
);
216 // We don't check for an invalid 'self' in an obj-c message expression to cut
217 // down false positives where logging functions get information from self
218 // (like its class) or doing "invalidation" on self when the initialization
222 void ObjCSelfInitChecker::PostVisitObjCIvarRefExpr(CheckerContext
&C
,
223 const ObjCIvarRefExpr
*E
) {
224 // FIXME: A callback should disable checkers at the start of functions.
225 if (!shouldRunOnFunctionOrMethod(dyn_cast
<NamedDecl
>(
226 C
.getCurrentAnalysisContext()->getDecl())))
229 checkForInvalidSelf(E
->getBase(), C
,
230 "Instance variable used while 'self' is not set to the result of "
231 "'[(super or self) init...]'");
234 void ObjCSelfInitChecker::PreVisitReturnStmt(CheckerContext
&C
,
235 const ReturnStmt
*S
) {
236 // FIXME: A callback should disable checkers at the start of functions.
237 if (!shouldRunOnFunctionOrMethod(dyn_cast
<NamedDecl
>(
238 C
.getCurrentAnalysisContext()->getDecl())))
241 checkForInvalidSelf(S
->getRetValue(), C
,
242 "Returning 'self' while it is not set to the result of "
243 "'[(super or self) init...]'");
246 // When a call receives a reference to 'self', [Pre/Post]VisitGenericCall pass
247 // the SelfFlags from the object 'self' point to before the call, to the new
248 // object after the call. This is to avoid invalidation of 'self' by logging
250 // Another common pattern in classes with multiple initializers is to put the
251 // subclass's common initialization bits into a static function that receives
252 // the value of 'self', e.g:
254 // if (!(self = [super init]))
256 // if (!(self = _commonInit(self)))
259 // Until we can use inter-procedural analysis, in such a call, transfer the
260 // SelfFlags to the result of the call.
262 void ObjCSelfInitChecker::PreVisitGenericCall(CheckerContext
&C
,
263 const CallExpr
*CE
) {
264 const GRState
*state
= C
.getState();
265 for (CallExpr::const_arg_iterator
266 I
= CE
->arg_begin(), E
= CE
->arg_end(); I
!= E
; ++I
) {
267 SVal argV
= state
->getSVal(*I
);
268 if (isSelfVar(argV
, C
)) {
269 preCallSelfFlags
= getSelfFlags(state
->getSVal(cast
<Loc
>(argV
)), C
);
271 } else if (hasSelfFlag(argV
, SelfFlag_Self
, C
)) {
272 preCallSelfFlags
= getSelfFlags(argV
, C
);
278 void ObjCSelfInitChecker::PostVisitGenericCall(CheckerContext
&C
,
279 const CallExpr
*CE
) {
280 const GRState
*state
= C
.getState();
281 for (CallExpr::const_arg_iterator
282 I
= CE
->arg_begin(), E
= CE
->arg_end(); I
!= E
; ++I
) {
283 SVal argV
= state
->getSVal(*I
);
284 if (isSelfVar(argV
, C
)) {
285 addSelfFlag(state
, state
->getSVal(cast
<Loc
>(argV
)), preCallSelfFlags
, C
);
287 } else if (hasSelfFlag(argV
, SelfFlag_Self
, C
)) {
288 addSelfFlag(state
, state
->getSVal(CE
), preCallSelfFlags
, C
);
294 void ObjCSelfInitChecker::visitLocation(CheckerContext
&C
, const Stmt
*S
,
295 SVal location
, bool isLoad
) {
296 // Tag the result of a load from 'self' so that we can easily know that the
297 // value is the object that 'self' points to.
298 const GRState
*state
= C
.getState();
299 if (isSelfVar(location
, C
))
300 addSelfFlag(state
, state
->getSVal(cast
<Loc
>(location
)), SelfFlag_Self
, C
);
303 // FIXME: A callback should disable checkers at the start of functions.
304 static bool shouldRunOnFunctionOrMethod(const NamedDecl
*ND
) {
308 const ObjCMethodDecl
*MD
= dyn_cast
<ObjCMethodDecl
>(ND
);
311 if (!isInitializationMethod(MD
))
314 // self = [super init] applies only to NSObject subclasses.
315 // For instance, NSProxy doesn't implement -init.
316 ASTContext
& Ctx
= MD
->getASTContext();
317 IdentifierInfo
* NSObjectII
= &Ctx
.Idents
.get("NSObject");
318 ObjCInterfaceDecl
* ID
= MD
->getClassInterface()->getSuperClass();
319 for ( ; ID
; ID
= ID
->getSuperClass()) {
320 IdentifierInfo
*II
= ID
->getIdentifier();
322 if (II
== NSObjectII
)
331 /// \brief Returns true if the location is 'self'.
332 static bool isSelfVar(SVal location
, CheckerContext
&C
) {
333 AnalysisContext
*analCtx
= C
.getCurrentAnalysisContext();
334 if (!analCtx
->getSelfDecl())
336 if (!isa
<loc::MemRegionVal
>(location
))
339 loc::MemRegionVal MRV
= cast
<loc::MemRegionVal
>(location
);
340 if (const DeclRegion
*DR
= dyn_cast
<DeclRegion
>(MRV
.getRegion()))
341 return (DR
->getDecl() == analCtx
->getSelfDecl());
346 static bool isInitializationMethod(const ObjCMethodDecl
*MD
) {
347 // Init methods with prefix like '-(id)_init' are private and the requirements
348 // are less strict so we don't check those.
349 return MD
->isInstanceMethod() &&
350 cocoa::deriveNamingConvention(MD
->getSelector(),
351 /*ignorePrefix=*/false) == cocoa::InitRule
;
354 static bool isInitMessage(const ObjCMessage
&msg
) {
355 return cocoa::deriveNamingConvention(msg
.getSelector()) == cocoa::InitRule
;