std::unique_ptr<BugType> MissingReleaseBugType;
std::unique_ptr<BugType> ExtraReleaseBugType;
+ std::unique_ptr<BugType> MistakenDeallocBugType;
public:
ObjCDeallocChecker();
bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M,
CheckerContext &C) const;
- SymbolRef getValueExplicitlyReleased(const ObjCMethodCall &M,
- CheckerContext &C) const;
+ bool diagnoseMistakenDealloc(SymbolRef DeallocedValue,
+ const ObjCMethodCall &M,
+ CheckerContext &C) const;
+
SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M,
CheckerContext &C) const;
const ObjCIvarRegion *getIvarRegionForIvarSymbol(SymbolRef IvarSym) const;
SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const;
+ const ObjCPropertyImplDecl*
+ findPropertyOnDeallocatingInstance(SymbolRef IvarSym,
+ CheckerContext &C) const;
+
ReleaseRequirement
getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const;
if (!instanceDeallocIsOnStack(C, DeallocedInstance))
return;
- SymbolRef ReleasedValue = getValueExplicitlyReleased(M, C);
+ SymbolRef ReleasedValue = nullptr;
+
+ if (M.getSelector() == ReleaseSel) {
+ ReleasedValue = M.getReceiverSVal().getAsSymbol();
+ } else if (M.getSelector() == DeallocSel && !M.isReceiverSelfOrSuper()) {
+ if (diagnoseMistakenDealloc(M.getReceiverSVal().getAsSymbol(), M, C))
+ return;
+ }
if (ReleasedValue) {
// An instance variable symbol was released with -release:
assert(!LCtx->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
}
+/// Given a symbol, determine whether the symbol refers to an ivar on
+/// the top-most deallocating instance. If so, find the property for that
+/// ivar, if one exists. Otherwise return null.
+const ObjCPropertyImplDecl *
+ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
+ SymbolRef IvarSym, CheckerContext &C) const {
+ SVal DeallocedInstance;
+ if (!isInInstanceDealloc(C, DeallocedInstance))
+ return nullptr;
+
+ // Try to get the region from which the ivar value was loaded.
+ auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
+ if (!IvarRegion)
+ return nullptr;
+
+ // Don't try to find the property if the ivar was not loaded from the
+ // given instance.
+ if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
+ IvarRegion->getSuperRegion())
+ return nullptr;
+
+ const LocationContext *LCtx = C.getLocationContext();
+ const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
+
+ const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
+ const ObjCPropertyImplDecl *PropImpl =
+ Container->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
+ return PropImpl;
+}
+
/// Emits a warning if the current context is -dealloc and ReleasedValue
/// must not be directly released in a -dealloc. Returns true if a diagnostic
/// was emitted.
bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
const ObjCMethodCall &M,
CheckerContext &C) const {
- SVal DeallocedInstance;
- if (!isInInstanceDealloc(C, DeallocedInstance))
- return false;
-
// Try to get the region from which the the released value was loaded.
// Note that, unlike diagnosing for missing releases, here we don't track
// values that must not be released in the state. This is because even if
// these values escape, it is still an error under the rules of MRR to
// release them in -dealloc.
- auto *ReleasedIvar = getIvarRegionForIvarSymbol(ReleasedValue);
- if (!ReleasedIvar)
- return false;
+ const ObjCPropertyImplDecl *PropImpl =
+ findPropertyOnDeallocatingInstance(ReleasedValue, C);
- if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
- ReleasedIvar->getSuperRegion())
+ if (!PropImpl)
return false;
- const LocationContext *LCtx = C.getLocationContext();
- const ObjCIvarDecl *ReleasedIvarDecl = ReleasedIvar->getDecl();
-
// If the ivar belongs to a property that must not be released directly
// in dealloc, emit a warning.
- const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
- const ObjCPropertyImplDecl *PropImpl =
- Container->FindPropertyImplIvarDecl(ReleasedIvarDecl->getIdentifier());
- if (!PropImpl)
- return false;
-
if (getDeallocReleaseRequirement(PropImpl) !=
ReleaseRequirement::MustNotReleaseDirectly) {
return false;
(PropDecl->getSetterKind() == ObjCPropertyDecl::Assign &&
!PropDecl->isReadOnly()));
+ const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext());
OS << "The '" << *PropImpl->getPropertyIvarDecl()
<< "' ivar in '" << *Container
<< "' was synthesized for ";
return true;
}
+/// Emits a warning if the current context is -dealloc and DeallocedValue
+/// must not be directly dealloced in a -dealloc. Returns true if a diagnostic
+/// was emitted.
+bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue,
+ const ObjCMethodCall &M,
+ CheckerContext &C) const {
+
+ // Find the property backing the instance variable that M
+ // is dealloc'ing.
+ const ObjCPropertyImplDecl *PropImpl =
+ findPropertyOnDeallocatingInstance(DeallocedValue, C);
+ if (!PropImpl)
+ return false;
+
+ if (getDeallocReleaseRequirement(PropImpl) !=
+ ReleaseRequirement::MustRelease) {
+ return false;
+ }
+
+ ExplodedNode *ErrNode = C.generateErrorNode();
+ if (!ErrNode)
+ return false;
+
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+
+ OS << "'" << *PropImpl->getPropertyIvarDecl()
+ << "' should be released rather than deallocated";
+
+ std::unique_ptr<BugReport> BR(
+ new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode));
+ BR->addRange(M.getOriginExpr()->getSourceRange());
+
+ C.emitReport(std::move(BR));
+
+ return true;
+}
ObjCDeallocChecker::
ObjCDeallocChecker()
ExtraReleaseBugType.reset(
new BugType(this, "Extra ivar release",
categories::MemoryCoreFoundationObjectiveC));
+
+ MistakenDeallocBugType.reset(
+ new BugType(this, "Mistaken dealloc",
+ categories::MemoryCoreFoundationObjectiveC));
}
void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
llvm_unreachable("Unrecognized setter kind");
}
-/// Returns the released value if M is a call to -release. Returns
-/// nullptr otherwise.
-SymbolRef
-ObjCDeallocChecker::getValueExplicitlyReleased(const ObjCMethodCall &M,
- CheckerContext &C) const {
- if (M.getSelector() != ReleaseSel)
- return nullptr;
-
- return M.getReceiverSVal().getAsSymbol();
-}
-
/// Returns the released value if M is a call a setter that releases
/// and nils out its underlying instance variable.
SymbolRef