diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/RetainCountChecker')
4 files changed, 93 insertions, 78 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index 6f8cb1432bb1..3f3267ff9391 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -12,12 +12,12 @@ //===----------------------------------------------------------------------===// #include "RetainCountChecker.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" using namespace clang; using namespace ento; using namespace retaincountchecker; -using llvm::StrInStrNoCase; REGISTER_MAP_WITH_PROGRAMSTATE(RefBindings, SymbolRef, RefVal) @@ -701,7 +701,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, for (ProgramStateRef St : Out) { if (DeallocSent) { - C.addTransition(St, C.getPredecessor(), &DeallocSentTag); + C.addTransition(St, C.getPredecessor(), &getDeallocSentTag()); } else { C.addTransition(St); } @@ -844,13 +844,13 @@ RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind, SymbolRef Sym) const { switch (ErrorKind) { case RefVal::ErrorUseAfterRelease: - return useAfterRelease; + return *UseAfterRelease; case RefVal::ErrorReleaseNotOwned: - return releaseNotOwned; + return *ReleaseNotOwned; case RefVal::ErrorDeallocNotOwned: if (Sym->getType()->getPointeeCXXRecordDecl()) - return freeNotOwned; - return deallocNotOwned; + return *FreeNotOwned; + return *DeallocNotOwned; default: llvm_unreachable("Unhandled error."); } @@ -946,7 +946,7 @@ bool RetainCountChecker::evalCall(const CallEvent &Call, // Assume that output is zero on the other branch. NullOutputState = NullOutputState->BindExpr( CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false); - C.addTransition(NullOutputState, &CastFailTag); + C.addTransition(NullOutputState, &getCastFailTag()); // And on the original branch assume that both input and // output are non-zero. @@ -1095,7 +1095,7 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, if (N) { const LangOptions &LOpts = C.getASTContext().getLangOpts(); auto R = - std::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C); + std::make_unique<RefLeakReport>(*LeakAtReturn, LOpts, N, Sym, C); C.emitReport(std::move(R)); } return N; @@ -1120,7 +1120,7 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); if (N) { auto R = std::make_unique<RefCountReport>( - returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); + *ReturnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); C.emitReport(std::move(R)); } return N; @@ -1273,8 +1273,8 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, os << "has a +" << V.getCount() << " retain count"; const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); - auto R = std::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym, - os.str()); + auto R = std::make_unique<RefCountReport>(*OverAutorelease, LOpts, N, Sym, + os.str()); Ctx.emitReport(std::move(R)); } @@ -1320,7 +1320,7 @@ RetainCountChecker::processLeaks(ProgramStateRef state, if (N) { for (SymbolRef L : Leaked) { - const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn; + const RefCountBug &BT = Pred ? *LeakWithinFunction : *LeakAtReturn; Ctx.emitReport(std::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx)); } } @@ -1473,48 +1473,73 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, // Checker registration. //===----------------------------------------------------------------------===// +std::unique_ptr<CheckerProgramPointTag> RetainCountChecker::DeallocSentTag; +std::unique_ptr<CheckerProgramPointTag> RetainCountChecker::CastFailTag; + void ento::registerRetainCountBase(CheckerManager &Mgr) { - Mgr.registerChecker<RetainCountChecker>(); + auto *Chk = Mgr.registerChecker<RetainCountChecker>(); + Chk->DeallocSentTag = + std::make_unique<CheckerProgramPointTag>(Chk, "DeallocSent"); + Chk->CastFailTag = + std::make_unique<CheckerProgramPointTag>(Chk, "DynamicCastFail"); } -bool ento::shouldRegisterRetainCountBase(const LangOptions &LO) { +bool ento::shouldRegisterRetainCountBase(const CheckerManager &mgr) { return true; } - -// FIXME: remove this, hack for backwards compatibility: -// it should be possible to enable the NS/CF retain count checker as -// osx.cocoa.RetainCount, and it should be possible to disable -// osx.OSObjectRetainCount using osx.cocoa.RetainCount:CheckOSObject=false. -static bool getOption(AnalyzerOptions &Options, - StringRef Postfix, - StringRef Value) { - auto I = Options.Config.find( - (StringRef("osx.cocoa.RetainCount:") + Postfix).str()); - if (I != Options.Config.end()) - return I->getValue() == Value; - return false; -} - void ento::registerRetainCountChecker(CheckerManager &Mgr) { auto *Chk = Mgr.getChecker<RetainCountChecker>(); Chk->TrackObjCAndCFObjects = true; - Chk->TrackNSCFStartParam = getOption(Mgr.getAnalyzerOptions(), - "TrackNSCFStartParam", - "true"); + Chk->TrackNSCFStartParam = Mgr.getAnalyzerOptions().getCheckerBooleanOption( + Mgr.getCurrentCheckerName(), "TrackNSCFStartParam"); + +#define INIT_BUGTYPE(KIND) \ + Chk->KIND = std::make_unique<RefCountBug>(Mgr.getCurrentCheckerName(), \ + RefCountBug::KIND); + // TODO: Ideally, we should have a checker for each of these bug types. + INIT_BUGTYPE(UseAfterRelease) + INIT_BUGTYPE(ReleaseNotOwned) + INIT_BUGTYPE(DeallocNotOwned) + INIT_BUGTYPE(FreeNotOwned) + INIT_BUGTYPE(OverAutorelease) + INIT_BUGTYPE(ReturnNotOwnedForOwned) + INIT_BUGTYPE(LeakWithinFunction) + INIT_BUGTYPE(LeakAtReturn) +#undef INIT_BUGTYPE } -bool ento::shouldRegisterRetainCountChecker(const LangOptions &LO) { +bool ento::shouldRegisterRetainCountChecker(const CheckerManager &mgr) { return true; } void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) { auto *Chk = Mgr.getChecker<RetainCountChecker>(); - if (!getOption(Mgr.getAnalyzerOptions(), - "CheckOSObject", - "false")) - Chk->TrackOSObjects = true; + Chk->TrackOSObjects = true; + + // FIXME: We want bug reports to always have the same checker name associated + // with them, yet here, if RetainCountChecker is disabled but + // OSObjectRetainCountChecker is enabled, the checker names will be different. + // This hack will make it so that the checker name depends on which checker is + // enabled rather than on the registration order. + // For the most part, we want **non-hidden checkers** to be associated with + // diagnostics, and **hidden checker options** with the fine-tuning of + // modeling. Following this logic, OSObjectRetainCountChecker should be the + // latter, but we can't just remove it for backward compatibility reasons. +#define LAZY_INIT_BUGTYPE(KIND) \ + if (!Chk->KIND) \ + Chk->KIND = std::make_unique<RefCountBug>(Mgr.getCurrentCheckerName(), \ + RefCountBug::KIND); + LAZY_INIT_BUGTYPE(UseAfterRelease) + LAZY_INIT_BUGTYPE(ReleaseNotOwned) + LAZY_INIT_BUGTYPE(DeallocNotOwned) + LAZY_INIT_BUGTYPE(FreeNotOwned) + LAZY_INIT_BUGTYPE(OverAutorelease) + LAZY_INIT_BUGTYPE(ReturnNotOwnedForOwned) + LAZY_INIT_BUGTYPE(LeakWithinFunction) + LAZY_INIT_BUGTYPE(LeakAtReturn) +#undef LAZY_INIT_BUGTYPE } -bool ento::shouldRegisterOSObjectRetainCountChecker(const LangOptions &LO) { +bool ento::shouldRegisterOSObjectRetainCountChecker(const CheckerManager &mgr) { return true; } diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h index dd79bbef321c..223e28c2c5b8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -251,20 +251,20 @@ class RetainCountChecker eval::Assume, eval::Call > { - RefCountBug useAfterRelease{this, RefCountBug::UseAfterRelease}; - RefCountBug releaseNotOwned{this, RefCountBug::ReleaseNotOwned}; - RefCountBug deallocNotOwned{this, RefCountBug::DeallocNotOwned}; - RefCountBug freeNotOwned{this, RefCountBug::FreeNotOwned}; - RefCountBug overAutorelease{this, RefCountBug::OverAutorelease}; - RefCountBug returnNotOwnedForOwned{this, RefCountBug::ReturnNotOwnedForOwned}; - RefCountBug leakWithinFunction{this, RefCountBug::LeakWithinFunction}; - RefCountBug leakAtReturn{this, RefCountBug::LeakAtReturn}; - - CheckerProgramPointTag DeallocSentTag{this, "DeallocSent"}; - CheckerProgramPointTag CastFailTag{this, "DynamicCastFail"}; +public: + std::unique_ptr<RefCountBug> UseAfterRelease; + std::unique_ptr<RefCountBug> ReleaseNotOwned; + std::unique_ptr<RefCountBug> DeallocNotOwned; + std::unique_ptr<RefCountBug> FreeNotOwned; + std::unique_ptr<RefCountBug> OverAutorelease; + std::unique_ptr<RefCountBug> ReturnNotOwnedForOwned; + std::unique_ptr<RefCountBug> LeakWithinFunction; + std::unique_ptr<RefCountBug> LeakAtReturn; mutable std::unique_ptr<RetainSummaryManager> Summaries; -public: + + static std::unique_ptr<CheckerProgramPointTag> DeallocSentTag; + static std::unique_ptr<CheckerProgramPointTag> CastFailTag; /// Track Objective-C and CoreFoundation objects. bool TrackObjCAndCFObjects = false; @@ -360,13 +360,11 @@ public: CheckerContext &Ctx, ExplodedNode *Pred = nullptr) const; - const CheckerProgramPointTag &getDeallocSentTag() const { - return DeallocSentTag; + static const CheckerProgramPointTag &getDeallocSentTag() { + return *DeallocSentTag; } - const CheckerProgramPointTag &getCastFailTag() const { - return CastFailTag; - } + static const CheckerProgramPointTag &getCastFailTag() { return *CastFailTag; } private: /// Perform the necessary checks and state adjustments at the end of the diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index 9853758f7f2c..1d8ed90f7590 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -18,7 +18,7 @@ using namespace clang; using namespace ento; using namespace retaincountchecker; -StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) { +StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugKind BT) { switch (BT) { case UseAfterRelease: return "Use-after-release"; @@ -37,7 +37,7 @@ StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) { case LeakAtReturn: return "Leak of returned object"; } - llvm_unreachable("Unknown RefCountBugType"); + llvm_unreachable("Unknown RefCountBugKind"); } StringRef RefCountBug::getDescription() const { @@ -60,13 +60,14 @@ StringRef RefCountBug::getDescription() const { case LeakAtReturn: return ""; } - llvm_unreachable("Unknown RefCountBugType"); + llvm_unreachable("Unknown RefCountBugKind"); } -RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT) +RefCountBug::RefCountBug(CheckerNameRef Checker, RefCountBugKind BT) : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount, - /*SuppressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn), - BT(BT), Checker(Checker) {} + /*SuppressOnSink=*/BT == LeakWithinFunction || + BT == LeakAtReturn), + BT(BT) {} static bool isNumericLiteralExpression(const Expr *E) { // FIXME: This set of cases was copied from SemaExprObjC. @@ -84,7 +85,7 @@ static std::string getPrettyTypeName(QualType QT) { QualType PT = QT->getPointeeType(); if (!PT.isNull() && !QT->getAs<TypedefType>()) if (const auto *RD = PT->getAsCXXRecordDecl()) - return RD->getName(); + return std::string(RD->getName()); return QT.getAsString(); } @@ -453,8 +454,6 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &BR) { const auto &BT = static_cast<const RefCountBug&>(BR.getBugType()); - const auto *Checker = - static_cast<const RetainCountChecker *>(BT.getChecker()); bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned || BT.getBugType() == RefCountBug::DeallocNotOwned; @@ -545,11 +544,11 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, const ProgramPointTag *Tag = N->getLocation().getTag(); - if (Tag == &Checker->getCastFailTag()) { + if (Tag == &RetainCountChecker::getCastFailTag()) { os << "Assuming dynamic cast returns null due to type mismatch"; } - if (Tag == &Checker->getDeallocSentTag()) { + if (Tag == &RetainCountChecker::getDeallocSentTag()) { // We only have summaries attached to nodes after evaluating CallExpr and // ObjCMessageExprs. const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h index e9e277754054..286a8ae2ef7d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h @@ -26,7 +26,7 @@ namespace retaincountchecker { class RefCountBug : public BugType { public: - enum RefCountBugType { + enum RefCountBugKind { UseAfterRelease, ReleaseNotOwned, DeallocNotOwned, @@ -36,21 +36,14 @@ public: LeakWithinFunction, LeakAtReturn, }; - RefCountBug(const CheckerBase *checker, RefCountBugType BT); + RefCountBug(CheckerNameRef Checker, RefCountBugKind BT); StringRef getDescription() const; - RefCountBugType getBugType() const { - return BT; - } - - const CheckerBase *getChecker() const { - return Checker; - } + RefCountBugKind getBugType() const { return BT; } private: - RefCountBugType BT; - const CheckerBase *Checker; - static StringRef bugTypeToName(RefCountBugType BT); + RefCountBugKind BT; + static StringRef bugTypeToName(RefCountBugKind BT); }; class RefCountReport : public PathSensitiveBugReport { |