blob: d1d6ef388db3383fc11dfd8818ef669808aa73a0 [file] [log] [blame]
Devin Coughlineb6673c2016-02-22 17:56:24 +00001//===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===//
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//
10// This defines ObjCSuperDeallocChecker, a builtin check that warns when
11// [super dealloc] is called twice on the same instance in MRR mode.
12//
13//===----------------------------------------------------------------------===//
14
15#include "ClangSACheckers.h"
16#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17#include "clang/StaticAnalyzer/Core/Checker.h"
18#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27class ObjCSuperDeallocChecker
28 : public Checker<check::PostObjCMessage, check::PreObjCMessage> {
29
30 mutable IdentifierInfo *IIdealloc, *IINSObject;
31 mutable Selector SELdealloc;
32
33 std::unique_ptr<BugType> DoubleSuperDeallocBugType;
34
35 void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
36
37 bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
38
39public:
40 ObjCSuperDeallocChecker();
41 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
42 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
43};
44
45} // End anonymous namespace.
46
47// Remember whether [super dealloc] has previously been called on the
48// a SymbolRef for the receiver.
49REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef)
50
51class SuperDeallocBRVisitor final
52 : public BugReporterVisitorImpl<SuperDeallocBRVisitor> {
53
54 SymbolRef ReceiverSymbol;
55 bool Satisfied;
56
57public:
58 SuperDeallocBRVisitor(SymbolRef ReceiverSymbol)
59 : ReceiverSymbol(ReceiverSymbol),
60 Satisfied(false) {}
61
62 PathDiagnosticPiece *VisitNode(const ExplodedNode *Succ,
63 const ExplodedNode *Pred,
64 BugReporterContext &BRC,
65 BugReport &BR) override;
66
67 void Profile(llvm::FoldingSetNodeID &ID) const override {
68 ID.Add(ReceiverSymbol);
69 }
70};
71
72void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M,
73 CheckerContext &C) const {
74 if (!isSuperDeallocMessage(M))
75 return;
76
77 ProgramStateRef State = C.getState();
78 SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol();
79 assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?");
80
81 bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);
82
83 // If [super dealloc] has not been called, there is nothing to do. We'll
84 // note the fact that [super dealloc] was called in checkPostObjCMessage.
85 if (!AlreadyCalled)
86 return;
87
88 // We have a duplicate [super dealloc] method call.
89 // This likely causes a crash, so stop exploring the
90 // path by generating a sink.
91 ExplodedNode *ErrNode = C.generateErrorNode();
92 // If we've already reached this node on another path, return.
93 if (!ErrNode)
94 return;
95
96 // Generate the report.
97 std::unique_ptr<BugReport> BR(
98 new BugReport(*DoubleSuperDeallocBugType,
99 "[super dealloc] should not be called multiple times",
100 ErrNode));
101 BR->addRange(M.getOriginExpr()->getSourceRange());
102 BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(ReceiverSymbol));
103 C.emitReport(std::move(BR));
104
105 return;
106}
107
108void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M,
109 CheckerContext &C) const {
110 // Check for [super dealloc] method call.
111 if (!isSuperDeallocMessage(M))
112 return;
113
114 ProgramStateRef State = C.getState();
115 SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol();
116 assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?");
117
118 // We add this transition in checkPostObjCMessage to avoid warning when
119 // we inline a call to [super dealloc] where the inlined call itself
120 // calls [super dealloc].
121 State = State->add<CalledSuperDealloc>(ReceiverSymbol);
122 C.addTransition(State);
123}
124
125ObjCSuperDeallocChecker::ObjCSuperDeallocChecker()
126 : IIdealloc(nullptr), IINSObject(nullptr) {
127
128 DoubleSuperDeallocBugType.reset(
129 new BugType(this, "[super dealloc] should not be called more than once",
130 categories::CoreFoundationObjectiveC));
131}
132
133void
134ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const {
135 if (IIdealloc)
136 return;
137
138 IIdealloc = &Ctx.Idents.get("dealloc");
139 IINSObject = &Ctx.Idents.get("NSObject");
140
141 SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc);
142}
143
144bool
145ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const {
146 if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
147 return false;
148
149 ASTContext &Ctx = M.getState()->getStateManager().getContext();
150 initIdentifierInfoAndSelectors(Ctx);
151
152 return M.getSelector() == SELdealloc;
153}
154
155PathDiagnosticPiece *SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ,
156 const ExplodedNode *Pred,
157 BugReporterContext &BRC,
158 BugReport &BR) {
159 if (Satisfied)
160 return nullptr;
161
162 ProgramStateRef State = Succ->getState();
163
164 bool CalledNow =
165 Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);
166 bool CalledBefore =
167 Pred->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);
168
169 // Is Succ the node on which the analyzer noted that [super dealloc] was
170 // called on ReceiverSymbol?
171 if (CalledNow && !CalledBefore) {
172 Satisfied = true;
173
174 ProgramPoint P = Succ->getLocation();
175 PathDiagnosticLocation L =
176 PathDiagnosticLocation::create(P, BRC.getSourceManager());
177
178 if (!L.isValid() || !L.asLocation().isValid())
179 return nullptr;
180
181 return new PathDiagnosticEventPiece(
182 L, "[super dealloc] called here");
183 }
184
185 return nullptr;
186}
187
188//===----------------------------------------------------------------------===//
189// Checker Registration.
190//===----------------------------------------------------------------------===//
191
192void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {
193 const LangOptions &LangOpts = Mgr.getLangOpts();
194 if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount)
195 return;
196 Mgr.registerChecker<ObjCSuperDeallocChecker>();
197}