xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp (revision 647cbc5de815c5651677bf8582797f716ec7b48d)
10b57cec5SDimitry Andric //== ObjCSelfInitChecker.cpp - Checker for 'self' initialization -*- C++ -*--=//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This defines ObjCSelfInitChecker, a builtin check that checks for uses of
100b57cec5SDimitry Andric // 'self' before proper initialization.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric // This checks initialization methods to verify that they assign 'self' to the
150b57cec5SDimitry Andric // result of an initialization call (e.g. [super init], or [self initWith..])
160b57cec5SDimitry Andric // before using 'self' or any instance variable.
170b57cec5SDimitry Andric //
180b57cec5SDimitry Andric // To perform the required checking, values are tagged with flags that indicate
190b57cec5SDimitry Andric // 1) if the object is the one pointed to by 'self', and 2) if the object
200b57cec5SDimitry Andric // is the result of an initializer (e.g. [super init]).
210b57cec5SDimitry Andric //
220b57cec5SDimitry Andric // Uses of an object that is true for 1) but not 2) trigger a diagnostic.
230b57cec5SDimitry Andric // The uses that are currently checked are:
240b57cec5SDimitry Andric //  - Using instance variables.
250b57cec5SDimitry Andric //  - Returning the object.
260b57cec5SDimitry Andric //
270b57cec5SDimitry Andric // Note that we don't check for an invalid 'self' that is the receiver of an
280b57cec5SDimitry Andric // obj-c message expression to cut down false positives where logging functions
290b57cec5SDimitry Andric // get information from self (like its class) or doing "invalidation" on self
300b57cec5SDimitry Andric // when the initialization fails.
310b57cec5SDimitry Andric //
320b57cec5SDimitry Andric // Because the object that 'self' points to gets invalidated when a call
330b57cec5SDimitry Andric // receives a reference to 'self', the checker keeps track and passes the flags
340b57cec5SDimitry Andric // for 1) and 2) to the new object that 'self' points to after the call.
350b57cec5SDimitry Andric //
360b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
390b57cec5SDimitry Andric #include "clang/AST/ParentMap.h"
400b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
410b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
420b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
430b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
440b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
450b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
460b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric using namespace clang;
490b57cec5SDimitry Andric using namespace ento;
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND);
520b57cec5SDimitry Andric static bool isInitializationMethod(const ObjCMethodDecl *MD);
530b57cec5SDimitry Andric static bool isInitMessage(const ObjCMethodCall &Msg);
540b57cec5SDimitry Andric static bool isSelfVar(SVal location, CheckerContext &C);
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric namespace {
570b57cec5SDimitry Andric class ObjCSelfInitChecker : public Checker<  check::PostObjCMessage,
580b57cec5SDimitry Andric                                              check::PostStmt<ObjCIvarRefExpr>,
590b57cec5SDimitry Andric                                              check::PreStmt<ReturnStmt>,
600b57cec5SDimitry Andric                                              check::PreCall,
610b57cec5SDimitry Andric                                              check::PostCall,
620b57cec5SDimitry Andric                                              check::Location,
630b57cec5SDimitry Andric                                              check::Bind > {
64*647cbc5dSDimitry Andric   const BugType BT{this, "Missing \"self = [(super or self) init...]\"",
65*647cbc5dSDimitry Andric                    categories::CoreFoundationObjectiveC};
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric   void checkForInvalidSelf(const Expr *E, CheckerContext &C,
680b57cec5SDimitry Andric                            const char *errorStr) const;
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric public:
710b57cec5SDimitry Andric   void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const;
720b57cec5SDimitry Andric   void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const;
730b57cec5SDimitry Andric   void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
740b57cec5SDimitry Andric   void checkLocation(SVal location, bool isLoad, const Stmt *S,
750b57cec5SDimitry Andric                      CheckerContext &C) const;
760b57cec5SDimitry Andric   void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
770b57cec5SDimitry Andric 
780b57cec5SDimitry Andric   void checkPreCall(const CallEvent &CE, CheckerContext &C) const;
790b57cec5SDimitry Andric   void checkPostCall(const CallEvent &CE, CheckerContext &C) const;
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric   void printState(raw_ostream &Out, ProgramStateRef State,
820b57cec5SDimitry Andric                   const char *NL, const char *Sep) const override;
830b57cec5SDimitry Andric };
840b57cec5SDimitry Andric } // end anonymous namespace
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric namespace {
870b57cec5SDimitry Andric enum SelfFlagEnum {
880b57cec5SDimitry Andric   /// No flag set.
890b57cec5SDimitry Andric   SelfFlag_None = 0x0,
900b57cec5SDimitry Andric   /// Value came from 'self'.
910b57cec5SDimitry Andric   SelfFlag_Self    = 0x1,
920b57cec5SDimitry Andric   /// Value came from the result of an initializer (e.g. [super init]).
930b57cec5SDimitry Andric   SelfFlag_InitRes = 0x2
940b57cec5SDimitry Andric };
950b57cec5SDimitry Andric }
960b57cec5SDimitry Andric 
REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag,SymbolRef,SelfFlagEnum)9781ad6265SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, SelfFlagEnum)
980b57cec5SDimitry Andric REGISTER_TRAIT_WITH_PROGRAMSTATE(CalledInit, bool)
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric /// A call receiving a reference to 'self' invalidates the object that
1010b57cec5SDimitry Andric /// 'self' contains. This keeps the "self flags" assigned to the 'self'
1020b57cec5SDimitry Andric /// object before the call so we can assign them to the new object that 'self'
1030b57cec5SDimitry Andric /// points to after the call.
10481ad6265SDimitry Andric REGISTER_TRAIT_WITH_PROGRAMSTATE(PreCallSelfFlags, SelfFlagEnum)
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) {
1070b57cec5SDimitry Andric   if (SymbolRef sym = val.getAsSymbol())
10881ad6265SDimitry Andric     if (const SelfFlagEnum *attachedFlags = state->get<SelfFlag>(sym))
10981ad6265SDimitry Andric       return *attachedFlags;
1100b57cec5SDimitry Andric   return SelfFlag_None;
1110b57cec5SDimitry Andric }
1120b57cec5SDimitry Andric 
getSelfFlags(SVal val,CheckerContext & C)1130b57cec5SDimitry Andric static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) {
1140b57cec5SDimitry Andric   return getSelfFlags(val, C.getState());
1150b57cec5SDimitry Andric }
1160b57cec5SDimitry Andric 
addSelfFlag(ProgramStateRef state,SVal val,SelfFlagEnum flag,CheckerContext & C)1170b57cec5SDimitry Andric static void addSelfFlag(ProgramStateRef state, SVal val,
1180b57cec5SDimitry Andric                         SelfFlagEnum flag, CheckerContext &C) {
1190b57cec5SDimitry Andric   // We tag the symbol that the SVal wraps.
1200b57cec5SDimitry Andric   if (SymbolRef sym = val.getAsSymbol()) {
12181ad6265SDimitry Andric     state = state->set<SelfFlag>(sym,
12281ad6265SDimitry Andric                                  SelfFlagEnum(getSelfFlags(val, state) | flag));
1230b57cec5SDimitry Andric     C.addTransition(state);
1240b57cec5SDimitry Andric   }
1250b57cec5SDimitry Andric }
1260b57cec5SDimitry Andric 
hasSelfFlag(SVal val,SelfFlagEnum flag,CheckerContext & C)1270b57cec5SDimitry Andric static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) {
1280b57cec5SDimitry Andric   return getSelfFlags(val, C) & flag;
1290b57cec5SDimitry Andric }
1300b57cec5SDimitry Andric 
1310b57cec5SDimitry Andric /// Returns true of the value of the expression is the object that 'self'
1320b57cec5SDimitry Andric /// points to and is an object that did not come from the result of calling
1330b57cec5SDimitry Andric /// an initializer.
isInvalidSelf(const Expr * E,CheckerContext & C)1340b57cec5SDimitry Andric static bool isInvalidSelf(const Expr *E, CheckerContext &C) {
1350b57cec5SDimitry Andric   SVal exprVal = C.getSVal(E);
1360b57cec5SDimitry Andric   if (!hasSelfFlag(exprVal, SelfFlag_Self, C))
1370b57cec5SDimitry Andric     return false; // value did not come from 'self'.
1380b57cec5SDimitry Andric   if (hasSelfFlag(exprVal, SelfFlag_InitRes, C))
1390b57cec5SDimitry Andric     return false; // 'self' is properly initialized.
1400b57cec5SDimitry Andric 
1410b57cec5SDimitry Andric   return true;
1420b57cec5SDimitry Andric }
1430b57cec5SDimitry Andric 
checkForInvalidSelf(const Expr * E,CheckerContext & C,const char * errorStr) const1440b57cec5SDimitry Andric void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C,
1450b57cec5SDimitry Andric                                               const char *errorStr) const {
1460b57cec5SDimitry Andric   if (!E)
1470b57cec5SDimitry Andric     return;
1480b57cec5SDimitry Andric 
1490b57cec5SDimitry Andric   if (!C.getState()->get<CalledInit>())
1500b57cec5SDimitry Andric     return;
1510b57cec5SDimitry Andric 
1520b57cec5SDimitry Andric   if (!isInvalidSelf(E, C))
1530b57cec5SDimitry Andric     return;
1540b57cec5SDimitry Andric 
1550b57cec5SDimitry Andric   // Generate an error node.
1560b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
1570b57cec5SDimitry Andric   if (!N)
1580b57cec5SDimitry Andric     return;
1590b57cec5SDimitry Andric 
160*647cbc5dSDimitry Andric   C.emitReport(std::make_unique<PathSensitiveBugReport>(BT, errorStr, N));
1610b57cec5SDimitry Andric }
1620b57cec5SDimitry Andric 
checkPostObjCMessage(const ObjCMethodCall & Msg,CheckerContext & C) const1630b57cec5SDimitry Andric void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
1640b57cec5SDimitry Andric                                                CheckerContext &C) const {
1650b57cec5SDimitry Andric   // When encountering a message that does initialization (init rule),
1660b57cec5SDimitry Andric   // tag the return value so that we know later on that if self has this value
1670b57cec5SDimitry Andric   // then it is properly initialized.
1680b57cec5SDimitry Andric 
1690b57cec5SDimitry Andric   // FIXME: A callback should disable checkers at the start of functions.
1700b57cec5SDimitry Andric   if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
1710b57cec5SDimitry Andric                                 C.getCurrentAnalysisDeclContext()->getDecl())))
1720b57cec5SDimitry Andric     return;
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric   if (isInitMessage(Msg)) {
1750b57cec5SDimitry Andric     // Tag the return value as the result of an initializer.
1760b57cec5SDimitry Andric     ProgramStateRef state = C.getState();
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric     // FIXME this really should be context sensitive, where we record
1790b57cec5SDimitry Andric     // the current stack frame (for IPA).  Also, we need to clean this
1800b57cec5SDimitry Andric     // value out when we return from this method.
1810b57cec5SDimitry Andric     state = state->set<CalledInit>(true);
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric     SVal V = C.getSVal(Msg.getOriginExpr());
1840b57cec5SDimitry Andric     addSelfFlag(state, V, SelfFlag_InitRes, C);
1850b57cec5SDimitry Andric     return;
1860b57cec5SDimitry Andric   }
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric   // We don't check for an invalid 'self' in an obj-c message expression to cut
1890b57cec5SDimitry Andric   // down false positives where logging functions get information from self
1900b57cec5SDimitry Andric   // (like its class) or doing "invalidation" on self when the initialization
1910b57cec5SDimitry Andric   // fails.
1920b57cec5SDimitry Andric }
1930b57cec5SDimitry Andric 
checkPostStmt(const ObjCIvarRefExpr * E,CheckerContext & C) const1940b57cec5SDimitry Andric void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E,
1950b57cec5SDimitry Andric                                         CheckerContext &C) const {
1960b57cec5SDimitry Andric   // FIXME: A callback should disable checkers at the start of functions.
1970b57cec5SDimitry Andric   if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
1980b57cec5SDimitry Andric                                  C.getCurrentAnalysisDeclContext()->getDecl())))
1990b57cec5SDimitry Andric     return;
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric   checkForInvalidSelf(
2020b57cec5SDimitry Andric       E->getBase(), C,
2030b57cec5SDimitry Andric       "Instance variable used while 'self' is not set to the result of "
2040b57cec5SDimitry Andric       "'[(super or self) init...]'");
2050b57cec5SDimitry Andric }
2060b57cec5SDimitry Andric 
checkPreStmt(const ReturnStmt * S,CheckerContext & C) const2070b57cec5SDimitry Andric void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,
2080b57cec5SDimitry Andric                                        CheckerContext &C) const {
2090b57cec5SDimitry Andric   // FIXME: A callback should disable checkers at the start of functions.
2100b57cec5SDimitry Andric   if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
2110b57cec5SDimitry Andric                                  C.getCurrentAnalysisDeclContext()->getDecl())))
2120b57cec5SDimitry Andric     return;
2130b57cec5SDimitry Andric 
2140b57cec5SDimitry Andric   checkForInvalidSelf(S->getRetValue(), C,
2150b57cec5SDimitry Andric                       "Returning 'self' while it is not set to the result of "
2160b57cec5SDimitry Andric                       "'[(super or self) init...]'");
2170b57cec5SDimitry Andric }
2180b57cec5SDimitry Andric 
2190b57cec5SDimitry Andric // When a call receives a reference to 'self', [Pre/Post]Call pass
2200b57cec5SDimitry Andric // the SelfFlags from the object 'self' points to before the call to the new
2210b57cec5SDimitry Andric // object after the call. This is to avoid invalidation of 'self' by logging
2220b57cec5SDimitry Andric // functions.
2230b57cec5SDimitry Andric // Another common pattern in classes with multiple initializers is to put the
2240b57cec5SDimitry Andric // subclass's common initialization bits into a static function that receives
2250b57cec5SDimitry Andric // the value of 'self', e.g:
2260b57cec5SDimitry Andric // @code
2270b57cec5SDimitry Andric //   if (!(self = [super init]))
2280b57cec5SDimitry Andric //     return nil;
2290b57cec5SDimitry Andric //   if (!(self = _commonInit(self)))
2300b57cec5SDimitry Andric //     return nil;
2310b57cec5SDimitry Andric // @endcode
2320b57cec5SDimitry Andric // Until we can use inter-procedural analysis, in such a call, transfer the
2330b57cec5SDimitry Andric // SelfFlags to the result of the call.
2340b57cec5SDimitry Andric 
checkPreCall(const CallEvent & CE,CheckerContext & C) const2350b57cec5SDimitry Andric void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE,
2360b57cec5SDimitry Andric                                        CheckerContext &C) const {
2370b57cec5SDimitry Andric   // FIXME: A callback should disable checkers at the start of functions.
2380b57cec5SDimitry Andric   if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
2390b57cec5SDimitry Andric                                  C.getCurrentAnalysisDeclContext()->getDecl())))
2400b57cec5SDimitry Andric     return;
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
2430b57cec5SDimitry Andric   unsigned NumArgs = CE.getNumArgs();
2440b57cec5SDimitry Andric   // If we passed 'self' as and argument to the call, record it in the state
2450b57cec5SDimitry Andric   // to be propagated after the call.
2460b57cec5SDimitry Andric   // Note, we could have just given up, but try to be more optimistic here and
2470b57cec5SDimitry Andric   // assume that the functions are going to continue initialization or will not
2480b57cec5SDimitry Andric   // modify self.
2490b57cec5SDimitry Andric   for (unsigned i = 0; i < NumArgs; ++i) {
2500b57cec5SDimitry Andric     SVal argV = CE.getArgSVal(i);
2510b57cec5SDimitry Andric     if (isSelfVar(argV, C)) {
25281ad6265SDimitry Andric       SelfFlagEnum selfFlags =
25381ad6265SDimitry Andric           getSelfFlags(state->getSVal(argV.castAs<Loc>()), C);
2540b57cec5SDimitry Andric       C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
2550b57cec5SDimitry Andric       return;
2560b57cec5SDimitry Andric     } else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
25781ad6265SDimitry Andric       SelfFlagEnum selfFlags = getSelfFlags(argV, C);
2580b57cec5SDimitry Andric       C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
2590b57cec5SDimitry Andric       return;
2600b57cec5SDimitry Andric     }
2610b57cec5SDimitry Andric   }
2620b57cec5SDimitry Andric }
2630b57cec5SDimitry Andric 
checkPostCall(const CallEvent & CE,CheckerContext & C) const2640b57cec5SDimitry Andric void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE,
2650b57cec5SDimitry Andric                                         CheckerContext &C) const {
2660b57cec5SDimitry Andric   // FIXME: A callback should disable checkers at the start of functions.
2670b57cec5SDimitry Andric   if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
2680b57cec5SDimitry Andric                                  C.getCurrentAnalysisDeclContext()->getDecl())))
2690b57cec5SDimitry Andric     return;
2700b57cec5SDimitry Andric 
2710b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
27281ad6265SDimitry Andric   SelfFlagEnum prevFlags = state->get<PreCallSelfFlags>();
2730b57cec5SDimitry Andric   if (!prevFlags)
2740b57cec5SDimitry Andric     return;
2750b57cec5SDimitry Andric   state = state->remove<PreCallSelfFlags>();
2760b57cec5SDimitry Andric 
2770b57cec5SDimitry Andric   unsigned NumArgs = CE.getNumArgs();
2780b57cec5SDimitry Andric   for (unsigned i = 0; i < NumArgs; ++i) {
2790b57cec5SDimitry Andric     SVal argV = CE.getArgSVal(i);
2800b57cec5SDimitry Andric     if (isSelfVar(argV, C)) {
2810b57cec5SDimitry Andric       // If the address of 'self' is being passed to the call, assume that the
2820b57cec5SDimitry Andric       // 'self' after the call will have the same flags.
2830b57cec5SDimitry Andric       // EX: log(&self)
2840b57cec5SDimitry Andric       addSelfFlag(state, state->getSVal(argV.castAs<Loc>()), prevFlags, C);
2850b57cec5SDimitry Andric       return;
2860b57cec5SDimitry Andric     } else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
2870b57cec5SDimitry Andric       // If 'self' is passed to the call by value, assume that the function
2880b57cec5SDimitry Andric       // returns 'self'. So assign the flags, which were set on 'self' to the
2890b57cec5SDimitry Andric       // return value.
2900b57cec5SDimitry Andric       // EX: self = performMoreInitialization(self)
2910b57cec5SDimitry Andric       addSelfFlag(state, CE.getReturnValue(), prevFlags, C);
2920b57cec5SDimitry Andric       return;
2930b57cec5SDimitry Andric     }
2940b57cec5SDimitry Andric   }
2950b57cec5SDimitry Andric 
2960b57cec5SDimitry Andric   C.addTransition(state);
2970b57cec5SDimitry Andric }
2980b57cec5SDimitry Andric 
checkLocation(SVal location,bool isLoad,const Stmt * S,CheckerContext & C) const2990b57cec5SDimitry Andric void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad,
3000b57cec5SDimitry Andric                                         const Stmt *S,
3010b57cec5SDimitry Andric                                         CheckerContext &C) const {
3020b57cec5SDimitry Andric   if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
3030b57cec5SDimitry Andric         C.getCurrentAnalysisDeclContext()->getDecl())))
3040b57cec5SDimitry Andric     return;
3050b57cec5SDimitry Andric 
3060b57cec5SDimitry Andric   // Tag the result of a load from 'self' so that we can easily know that the
3070b57cec5SDimitry Andric   // value is the object that 'self' points to.
3080b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
3090b57cec5SDimitry Andric   if (isSelfVar(location, C))
3100b57cec5SDimitry Andric     addSelfFlag(state, state->getSVal(location.castAs<Loc>()), SelfFlag_Self,
3110b57cec5SDimitry Andric                 C);
3120b57cec5SDimitry Andric }
3130b57cec5SDimitry Andric 
3140b57cec5SDimitry Andric 
checkBind(SVal loc,SVal val,const Stmt * S,CheckerContext & C) const3150b57cec5SDimitry Andric void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S,
3160b57cec5SDimitry Andric                                     CheckerContext &C) const {
3170b57cec5SDimitry Andric   // Allow assignment of anything to self. Self is a local variable in the
3180b57cec5SDimitry Andric   // initializer, so it is legal to assign anything to it, like results of
3190b57cec5SDimitry Andric   // static functions/method calls. After self is assigned something we cannot
3200b57cec5SDimitry Andric   // reason about, stop enforcing the rules.
3210b57cec5SDimitry Andric   // (Only continue checking if the assigned value should be treated as self.)
3220b57cec5SDimitry Andric   if ((isSelfVar(loc, C)) &&
3230b57cec5SDimitry Andric       !hasSelfFlag(val, SelfFlag_InitRes, C) &&
3240b57cec5SDimitry Andric       !hasSelfFlag(val, SelfFlag_Self, C) &&
3250b57cec5SDimitry Andric       !isSelfVar(val, C)) {
3260b57cec5SDimitry Andric 
3270b57cec5SDimitry Andric     // Stop tracking the checker-specific state in the state.
3280b57cec5SDimitry Andric     ProgramStateRef State = C.getState();
3290b57cec5SDimitry Andric     State = State->remove<CalledInit>();
3300b57cec5SDimitry Andric     if (SymbolRef sym = loc.getAsSymbol())
3310b57cec5SDimitry Andric       State = State->remove<SelfFlag>(sym);
3320b57cec5SDimitry Andric     C.addTransition(State);
3330b57cec5SDimitry Andric   }
3340b57cec5SDimitry Andric }
3350b57cec5SDimitry Andric 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const3360b57cec5SDimitry Andric void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State,
3370b57cec5SDimitry Andric                                      const char *NL, const char *Sep) const {
3380b57cec5SDimitry Andric   SelfFlagTy FlagMap = State->get<SelfFlag>();
3390b57cec5SDimitry Andric   bool DidCallInit = State->get<CalledInit>();
34081ad6265SDimitry Andric   SelfFlagEnum PreCallFlags = State->get<PreCallSelfFlags>();
3410b57cec5SDimitry Andric 
3420b57cec5SDimitry Andric   if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags)
3430b57cec5SDimitry Andric     return;
3440b57cec5SDimitry Andric 
3450b57cec5SDimitry Andric   Out << Sep << NL << *this << " :" << NL;
3460b57cec5SDimitry Andric 
3470b57cec5SDimitry Andric   if (DidCallInit)
3480b57cec5SDimitry Andric     Out << "  An init method has been called." << NL;
3490b57cec5SDimitry Andric 
3500b57cec5SDimitry Andric   if (PreCallFlags != SelfFlag_None) {
3510b57cec5SDimitry Andric     if (PreCallFlags & SelfFlag_Self) {
3520b57cec5SDimitry Andric       Out << "  An argument of the current call came from the 'self' variable."
3530b57cec5SDimitry Andric           << NL;
3540b57cec5SDimitry Andric     }
3550b57cec5SDimitry Andric     if (PreCallFlags & SelfFlag_InitRes) {
3560b57cec5SDimitry Andric       Out << "  An argument of the current call came from an init method."
3570b57cec5SDimitry Andric           << NL;
3580b57cec5SDimitry Andric     }
3590b57cec5SDimitry Andric   }
3600b57cec5SDimitry Andric 
3610b57cec5SDimitry Andric   Out << NL;
36206c3fb27SDimitry Andric   for (auto [Sym, Flag] : FlagMap) {
36306c3fb27SDimitry Andric     Out << Sym << " : ";
3640b57cec5SDimitry Andric 
36506c3fb27SDimitry Andric     if (Flag == SelfFlag_None)
3660b57cec5SDimitry Andric       Out << "none";
3670b57cec5SDimitry Andric 
36806c3fb27SDimitry Andric     if (Flag & SelfFlag_Self)
3690b57cec5SDimitry Andric       Out << "self variable";
3700b57cec5SDimitry Andric 
37106c3fb27SDimitry Andric     if (Flag & SelfFlag_InitRes) {
37206c3fb27SDimitry Andric       if (Flag != SelfFlag_InitRes)
3730b57cec5SDimitry Andric         Out << " | ";
3740b57cec5SDimitry Andric       Out << "result of init method";
3750b57cec5SDimitry Andric     }
3760b57cec5SDimitry Andric 
3770b57cec5SDimitry Andric     Out << NL;
3780b57cec5SDimitry Andric   }
3790b57cec5SDimitry Andric }
3800b57cec5SDimitry Andric 
3810b57cec5SDimitry Andric 
3820b57cec5SDimitry Andric // FIXME: A callback should disable checkers at the start of functions.
shouldRunOnFunctionOrMethod(const NamedDecl * ND)3830b57cec5SDimitry Andric static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) {
3840b57cec5SDimitry Andric   if (!ND)
3850b57cec5SDimitry Andric     return false;
3860b57cec5SDimitry Andric 
3870b57cec5SDimitry Andric   const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ND);
3880b57cec5SDimitry Andric   if (!MD)
3890b57cec5SDimitry Andric     return false;
3900b57cec5SDimitry Andric   if (!isInitializationMethod(MD))
3910b57cec5SDimitry Andric     return false;
3920b57cec5SDimitry Andric 
3930b57cec5SDimitry Andric   // self = [super init] applies only to NSObject subclasses.
3940b57cec5SDimitry Andric   // For instance, NSProxy doesn't implement -init.
3950b57cec5SDimitry Andric   ASTContext &Ctx = MD->getASTContext();
3960b57cec5SDimitry Andric   IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
3970b57cec5SDimitry Andric   ObjCInterfaceDecl *ID = MD->getClassInterface()->getSuperClass();
3980b57cec5SDimitry Andric   for ( ; ID ; ID = ID->getSuperClass()) {
3990b57cec5SDimitry Andric     IdentifierInfo *II = ID->getIdentifier();
4000b57cec5SDimitry Andric 
4010b57cec5SDimitry Andric     if (II == NSObjectII)
4020b57cec5SDimitry Andric       break;
4030b57cec5SDimitry Andric   }
4040b57cec5SDimitry Andric   return ID != nullptr;
4050b57cec5SDimitry Andric }
4060b57cec5SDimitry Andric 
4070b57cec5SDimitry Andric /// Returns true if the location is 'self'.
isSelfVar(SVal location,CheckerContext & C)4080b57cec5SDimitry Andric static bool isSelfVar(SVal location, CheckerContext &C) {
4090b57cec5SDimitry Andric   AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext();
4100b57cec5SDimitry Andric   if (!analCtx->getSelfDecl())
4110b57cec5SDimitry Andric     return false;
41281ad6265SDimitry Andric   if (!isa<loc::MemRegionVal>(location))
4130b57cec5SDimitry Andric     return false;
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric   loc::MemRegionVal MRV = location.castAs<loc::MemRegionVal>();
4160b57cec5SDimitry Andric   if (const DeclRegion *DR = dyn_cast<DeclRegion>(MRV.stripCasts()))
4170b57cec5SDimitry Andric     return (DR->getDecl() == analCtx->getSelfDecl());
4180b57cec5SDimitry Andric 
4190b57cec5SDimitry Andric   return false;
4200b57cec5SDimitry Andric }
4210b57cec5SDimitry Andric 
isInitializationMethod(const ObjCMethodDecl * MD)4220b57cec5SDimitry Andric static bool isInitializationMethod(const ObjCMethodDecl *MD) {
4230b57cec5SDimitry Andric   return MD->getMethodFamily() == OMF_init;
4240b57cec5SDimitry Andric }
4250b57cec5SDimitry Andric 
isInitMessage(const ObjCMethodCall & Call)4260b57cec5SDimitry Andric static bool isInitMessage(const ObjCMethodCall &Call) {
4270b57cec5SDimitry Andric   return Call.getMethodFamily() == OMF_init;
4280b57cec5SDimitry Andric }
4290b57cec5SDimitry Andric 
4300b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4310b57cec5SDimitry Andric // Registration.
4320b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4330b57cec5SDimitry Andric 
registerObjCSelfInitChecker(CheckerManager & mgr)4340b57cec5SDimitry Andric void ento::registerObjCSelfInitChecker(CheckerManager &mgr) {
4350b57cec5SDimitry Andric   mgr.registerChecker<ObjCSelfInitChecker>();
4360b57cec5SDimitry Andric }
4370b57cec5SDimitry Andric 
shouldRegisterObjCSelfInitChecker(const CheckerManager & mgr)4385ffd83dbSDimitry Andric bool ento::shouldRegisterObjCSelfInitChecker(const CheckerManager &mgr) {
4390b57cec5SDimitry Andric   return true;
4400b57cec5SDimitry Andric }
441