blob: d1d6ef388db3383fc11dfd8818ef669808aa73a0 [file] [log] [blame]
//===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This defines ObjCSuperDeallocChecker, a builtin check that warns when
// [super dealloc] is called twice on the same instance in MRR mode.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
using namespace clang;
using namespace ento;
namespace {
class ObjCSuperDeallocChecker
: public Checker<check::PostObjCMessage, check::PreObjCMessage> {
mutable IdentifierInfo *IIdealloc, *IINSObject;
mutable Selector SELdealloc;
std::unique_ptr<BugType> DoubleSuperDeallocBugType;
void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
public:
ObjCSuperDeallocChecker();
void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
};
} // End anonymous namespace.
// Remember whether [super dealloc] has previously been called on the
// a SymbolRef for the receiver.
REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef)
class SuperDeallocBRVisitor final
: public BugReporterVisitorImpl<SuperDeallocBRVisitor> {
SymbolRef ReceiverSymbol;
bool Satisfied;
public:
SuperDeallocBRVisitor(SymbolRef ReceiverSymbol)
: ReceiverSymbol(ReceiverSymbol),
Satisfied(false) {}
PathDiagnosticPiece *VisitNode(const ExplodedNode *Succ,
const ExplodedNode *Pred,
BugReporterContext &BRC,
BugReport &BR) override;
void Profile(llvm::FoldingSetNodeID &ID) const override {
ID.Add(ReceiverSymbol);
}
};
void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M,
CheckerContext &C) const {
if (!isSuperDeallocMessage(M))
return;
ProgramStateRef State = C.getState();
SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol();
assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?");
bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);
// If [super dealloc] has not been called, there is nothing to do. We'll
// note the fact that [super dealloc] was called in checkPostObjCMessage.
if (!AlreadyCalled)
return;
// We have a duplicate [super dealloc] method call.
// This likely causes a crash, so stop exploring the
// path by generating a sink.
ExplodedNode *ErrNode = C.generateErrorNode();
// If we've already reached this node on another path, return.
if (!ErrNode)
return;
// Generate the report.
std::unique_ptr<BugReport> BR(
new BugReport(*DoubleSuperDeallocBugType,
"[super dealloc] should not be called multiple times",
ErrNode));
BR->addRange(M.getOriginExpr()->getSourceRange());
BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(ReceiverSymbol));
C.emitReport(std::move(BR));
return;
}
void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M,
CheckerContext &C) const {
// Check for [super dealloc] method call.
if (!isSuperDeallocMessage(M))
return;
ProgramStateRef State = C.getState();
SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol();
assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?");
// We add this transition in checkPostObjCMessage to avoid warning when
// we inline a call to [super dealloc] where the inlined call itself
// calls [super dealloc].
State = State->add<CalledSuperDealloc>(ReceiverSymbol);
C.addTransition(State);
}
ObjCSuperDeallocChecker::ObjCSuperDeallocChecker()
: IIdealloc(nullptr), IINSObject(nullptr) {
DoubleSuperDeallocBugType.reset(
new BugType(this, "[super dealloc] should not be called more than once",
categories::CoreFoundationObjectiveC));
}
void
ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const {
if (IIdealloc)
return;
IIdealloc = &Ctx.Idents.get("dealloc");
IINSObject = &Ctx.Idents.get("NSObject");
SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc);
}
bool
ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const {
if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
return false;
ASTContext &Ctx = M.getState()->getStateManager().getContext();
initIdentifierInfoAndSelectors(Ctx);
return M.getSelector() == SELdealloc;
}
PathDiagnosticPiece *SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ,
const ExplodedNode *Pred,
BugReporterContext &BRC,
BugReport &BR) {
if (Satisfied)
return nullptr;
ProgramStateRef State = Succ->getState();
bool CalledNow =
Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);
bool CalledBefore =
Pred->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);
// Is Succ the node on which the analyzer noted that [super dealloc] was
// called on ReceiverSymbol?
if (CalledNow && !CalledBefore) {
Satisfied = true;
ProgramPoint P = Succ->getLocation();
PathDiagnosticLocation L =
PathDiagnosticLocation::create(P, BRC.getSourceManager());
if (!L.isValid() || !L.asLocation().isValid())
return nullptr;
return new PathDiagnosticEventPiece(
L, "[super dealloc] called here");
}
return nullptr;
}
//===----------------------------------------------------------------------===//
// Checker Registration.
//===----------------------------------------------------------------------===//
void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {
const LangOptions &LangOpts = Mgr.getLangOpts();
if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount)
return;
Mgr.registerChecker<ObjCSuperDeallocChecker>();
}