23 using namespace clang;
27 class ObjCSuperDeallocChecker
28 :
public Checker<check::PostObjCMessage, check::PreObjCMessage,
29 check::PreCall, check::Location> {
34 std::unique_ptr<BugType> DoubleSuperDeallocBugType;
36 void initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const;
41 ObjCSuperDeallocChecker();
42 void checkPostObjCMessage(
const ObjCMethodCall &M, CheckerContext &C)
const;
43 void checkPreObjCMessage(
const ObjCMethodCall &M, CheckerContext &C)
const;
45 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
47 void checkLocation(SVal l,
bool isLoad,
const Stmt *S,
48 CheckerContext &C)
const;
52 void diagnoseCallArguments(
const CallEvent &CE, CheckerContext &C)
const;
54 void reportUseAfterDealloc(
SymbolRef Sym, StringRef Desc,
const Stmt *S,
55 CheckerContext &C)
const;
70 SuperDeallocBRVisitor(
SymbolRef ReceiverSymbol)
71 : ReceiverSymbol(ReceiverSymbol),
74 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *Succ,
75 BugReporterContext &BRC,
76 BugReport &BR)
override;
78 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
79 ID.Add(ReceiverSymbol);
84 void ObjCSuperDeallocChecker::checkPreObjCMessage(
const ObjCMethodCall &M,
85 CheckerContext &C)
const {
89 if (!ReceiverSymbol) {
90 diagnoseCallArguments(M, C);
94 bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);
100 if (isSuperDeallocMessage(M)) {
101 Desc =
"[super dealloc] should not be called multiple times";
106 reportUseAfterDealloc(ReceiverSymbol, Desc, M.
getOriginExpr(), C);
109 void ObjCSuperDeallocChecker::checkPreCall(
const CallEvent &Call,
110 CheckerContext &C)
const {
111 diagnoseCallArguments(Call, C);
114 void ObjCSuperDeallocChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
115 CheckerContext &C)
const {
117 if (!isSuperDeallocMessage(M))
122 assert(ReceiverSymbol &&
"No receiver symbol at call to [super dealloc]?");
127 State = State->add<CalledSuperDealloc>(ReceiverSymbol);
128 C.addTransition(State);
131 void ObjCSuperDeallocChecker::checkLocation(SVal L,
bool IsLoad,
const Stmt *S,
132 CheckerContext &C)
const {
133 SymbolRef BaseSym = L.getLocSymbolInBase();
139 if (!State->contains<CalledSuperDealloc>(BaseSym))
142 const MemRegion *R = L.getAsRegion();
148 const MemRegion *PriorSubRegion =
nullptr;
149 while (
const SubRegion *SR = dyn_cast<SubRegion>(R)) {
150 if (
const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) {
151 BaseSym = SymR->getSymbol();
154 R = SR->getSuperRegion();
159 StringRef Desc = StringRef();
160 auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion);
163 llvm::raw_string_ostream
OS(Buf);
165 OS <<
"Use of instance variable '" << *IvarRegion->getDecl() <<
166 "' after 'self' has been deallocated";
170 reportUseAfterDealloc(BaseSym, Desc, S, C);
176 void ObjCSuperDeallocChecker::reportUseAfterDealloc(
SymbolRef Sym,
179 CheckerContext &C)
const {
183 ExplodedNode *ErrNode = C.generateErrorNode();
189 Desc =
"Use of 'self' after it has been deallocated";
192 std::unique_ptr<BugReport> BR(
193 new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode));
195 BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(Sym));
196 C.emitReport(std::move(BR));
201 void ObjCSuperDeallocChecker::diagnoseCallArguments(
const CallEvent &CE,
202 CheckerContext &C)
const {
204 unsigned ArgCount = CE.getNumArgs();
205 for (
unsigned I = 0; I < ArgCount; I++) {
206 SymbolRef Sym = CE.getArgSVal(I).getAsSymbol();
210 if (State->contains<CalledSuperDealloc>(Sym)) {
211 reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C);
217 ObjCSuperDeallocChecker::ObjCSuperDeallocChecker()
218 : IIdealloc(nullptr), IINSObject(nullptr) {
220 DoubleSuperDeallocBugType.reset(
221 new BugType(
this,
"[super dealloc] should not be called more than once",
226 ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(
ASTContext &Ctx)
const {
231 IINSObject = &Ctx.
Idents.
get(
"NSObject");
237 ObjCSuperDeallocChecker::isSuperDeallocMessage(
const ObjCMethodCall &M)
const {
241 ASTContext &Ctx = M.getState()->getStateManager().getContext();
242 initIdentifierInfoAndSelectors(Ctx);
247 std::shared_ptr<PathDiagnosticPiece>
248 SuperDeallocBRVisitor::VisitNode(
const ExplodedNode *Succ,
249 BugReporterContext &BRC, BugReport &) {
256 Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);
258 Succ->getFirstPred()->getState()->contains<CalledSuperDealloc>(
263 if (CalledNow && !CalledBefore) {
267 PathDiagnosticLocation L =
270 if (!L.isValid() || !L.asLocation().isValid())
273 return std::make_shared<PathDiagnosticEventPiece>(
274 L,
"[super dealloc] called here");
284 void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {
286 if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount)
288 Mgr.registerChecker<ObjCSuperDeallocChecker>();
SVal getSelfSVal() const
Return the value of 'self' if available.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
const char *const CoreFoundationObjectiveC
Smart pointer class that efficiently represents Objective-C method names.
const SymExpr * SymbolRef
Stmt - This represents one statement.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Represents any expression that calls an Objective-C method.
ReceiverKind getReceiverKind() const
Determine the kind of receiver that this message is being sent to.
SelectorTable & Selectors
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
virtual const ObjCMessageExpr * getOriginExpr() const
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
Selector getSelector() const
Dataflow Directional Tag Classes.
std::unique_ptr< DiagnosticConsumer > create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords=false)
Returns a DiagnosticConsumer that serializes diagnostics to a bitcode file.
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
Selector getSelector(unsigned NumArgs, IdentifierInfo **IIV)
Can create any sort of selector.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...