diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index 62bc3218d9ced..65ff902ef1755 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -840,20 +840,27 @@ ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state, const RefCountBug & RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind, SymbolRef Sym) const { + const RefCountFrontend &FE = getPreferredFrontend(); + switch (ErrorKind) { case RefVal::ErrorUseAfterRelease: - return *UseAfterRelease; + return FE.UseAfterRelease; case RefVal::ErrorReleaseNotOwned: - return *ReleaseNotOwned; + return FE.ReleaseNotOwned; case RefVal::ErrorDeallocNotOwned: if (Sym->getType()->getPointeeCXXRecordDecl()) - return *FreeNotOwned; - return *DeallocNotOwned; + return FE.FreeNotOwned; + return FE.DeallocNotOwned; default: llvm_unreachable("Unhandled error."); } } +bool RetainCountChecker::isReleaseUnownedError(RefVal::Kind ErrorKind) const { + return ErrorKind == RefVal::ErrorReleaseNotOwned || + ErrorKind == RefVal::ErrorDeallocNotOwned; +} + void RetainCountChecker::processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, @@ -874,8 +881,8 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St, return; auto report = std::make_unique( - errorKindToBugKind(ErrorKind, Sym), - C.getASTContext().getLangOpts(), N, Sym); + errorKindToBugKind(ErrorKind, Sym), C.getASTContext().getLangOpts(), N, + Sym, /*isLeak=*/false, isReleaseUnownedError(ErrorKind)); report->addRange(ErrorRange); C.emitReport(std::move(report)); } @@ -1090,8 +1097,8 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *N = C.addTransition(state, Pred); if (N) { const LangOptions &LOpts = C.getASTContext().getLangOpts(); - auto R = - std::make_unique(*LeakAtReturn, LOpts, N, Sym, C); + auto R = std::make_unique( + getPreferredFrontend().LeakAtReturn, LOpts, N, Sym, C); C.emitReport(std::move(R)); } return N; @@ -1113,7 +1120,8 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *N = C.addTransition(state, Pred); if (N) { auto R = std::make_unique( - *ReturnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); + getPreferredFrontend().ReturnNotOwnedForOwned, + C.getASTContext().getLangOpts(), N, Sym); C.emitReport(std::move(R)); } return N; @@ -1261,8 +1269,8 @@ ProgramStateRef RetainCountChecker::handleAutoreleaseCounts( os << "has a +" << V.getCount() << " retain count"; const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); - auto R = std::make_unique(*OverAutorelease, LOpts, N, Sym, - os.str()); + auto R = std::make_unique( + getPreferredFrontend().OverAutorelease, LOpts, N, Sym, os.str()); Ctx.emitReport(std::move(R)); } @@ -1307,8 +1315,10 @@ RetainCountChecker::processLeaks(ProgramStateRef state, const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); if (N) { + const RefCountFrontend &FE = getPreferredFrontend(); + const RefCountBug &BT = Pred ? FE.LeakWithinFunction : FE.LeakAtReturn; + for (SymbolRef L : Leaked) { - const RefCountBug &BT = Pred ? *LeakWithinFunction : *LeakAtReturn; Ctx.emitReport(std::make_unique(BT, LOpts, N, L, Ctx)); } } @@ -1463,44 +1473,31 @@ std::unique_ptr RetainCountChecker::DeallocSentTag; std::unique_ptr RetainCountChecker::CastFailTag; void ento::registerRetainCountBase(CheckerManager &Mgr) { - auto *Chk = Mgr.registerChecker(); + auto *Chk = Mgr.getChecker(); Chk->DeallocSentTag = std::make_unique( "RetainCountChecker", "DeallocSent"); Chk->CastFailTag = std::make_unique( "RetainCountChecker", "DynamicCastFail"); } -bool ento::shouldRegisterRetainCountBase(const CheckerManager &mgr) { +bool ento::shouldRegisterRetainCountBase(const CheckerManager &) { return true; } + void ento::registerRetainCountChecker(CheckerManager &Mgr) { auto *Chk = Mgr.getChecker(); - Chk->TrackObjCAndCFObjects = true; + Chk->RetainCount.enable(Mgr); Chk->TrackNSCFStartParam = Mgr.getAnalyzerOptions().getCheckerBooleanOption( Mgr.getCurrentCheckerName(), "TrackNSCFStartParam"); - -#define INIT_BUGTYPE(KIND) \ - Chk->KIND = std::make_unique(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 CheckerManager &mgr) { +bool ento::shouldRegisterRetainCountChecker(const CheckerManager &) { return true; } void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) { auto *Chk = Mgr.getChecker(); - Chk->TrackOSObjects = true; + Chk->OSObjectRetainCount.enable(Mgr); // FIXME: We want bug reports to always have the same checker name associated // with them, yet here, if RetainCountChecker is disabled but @@ -1511,21 +1508,8 @@ void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) { // 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(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 CheckerManager &mgr) { +bool ento::shouldRegisterOSObjectRetainCountChecker(const CheckerManager &) { return true; } diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h index 0e811436605ff..8854e1062fc1d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -235,51 +235,32 @@ class RefVal { }; class RetainCountChecker - : public Checker< check::Bind, - check::DeadSymbols, - check::BeginFunction, - check::EndFunction, - check::PostStmt, - check::PostStmt, - check::PostStmt, - check::PostStmt, - check::PostStmt, - check::PostStmt, - check::PostCall, - check::RegionChanges, - eval::Assume, - eval::Call > { + : public CheckerFamily< + check::Bind, check::DeadSymbols, check::BeginFunction, + check::EndFunction, check::PostStmt, + check::PostStmt, check::PostStmt, + check::PostStmt, + check::PostStmt, check::PostStmt, + check::PostCall, check::RegionChanges, eval::Assume, eval::Call> { public: - std::unique_ptr UseAfterRelease; - std::unique_ptr ReleaseNotOwned; - std::unique_ptr DeallocNotOwned; - std::unique_ptr FreeNotOwned; - std::unique_ptr OverAutorelease; - std::unique_ptr ReturnNotOwnedForOwned; - std::unique_ptr LeakWithinFunction; - std::unique_ptr LeakAtReturn; + RefCountFrontend RetainCount; + RefCountFrontend OSObjectRetainCount; mutable std::unique_ptr Summaries; static std::unique_ptr DeallocSentTag; static std::unique_ptr CastFailTag; - /// Track Objective-C and CoreFoundation objects. - bool TrackObjCAndCFObjects = false; - - /// Track sublcasses of OSObject. - bool TrackOSObjects = false; - /// Track initial parameters (for the entry point) for NS/CF objects. bool TrackNSCFStartParam = false; - RetainCountChecker() {}; + StringRef getDebugTag() const override { return "RetainCountChecker"; } RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const { if (!Summaries) - Summaries.reset( - new RetainSummaryManager(Ctx, TrackObjCAndCFObjects, TrackOSObjects)); + Summaries = std::make_unique( + Ctx, RetainCount.isEnabled(), OSObjectRetainCount.isEnabled()); return *Summaries; } @@ -287,6 +268,15 @@ class RetainCountChecker return getSummaryManager(C.getASTContext()); } + const RefCountFrontend &getPreferredFrontend() const { + // FIXME: The two frontends of this checker family are in an unusual + // relationship: if they are both enabled, then all bug reports are + // reported by RetainCount (i.e. `osx.cocoa.RetainCount`), even the bugs + // that "belong to" OSObjectRetainCount (i.e. `osx.OSObjectRetainCount`). + // This is counter-intuitive and should be fixed to avoid confusion. + return RetainCount.isEnabled() ? RetainCount : OSObjectRetainCount; + } + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const override; @@ -337,6 +327,8 @@ class RetainCountChecker const RefCountBug &errorKindToBugKind(RefVal::Kind ErrorKind, SymbolRef Sym) const; + bool isReleaseUnownedError(RefVal::Kind ErrorKind) const; + void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, SymbolRef Sym, CheckerContext &C) const; diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index c9f5dc99aaf6b..cad2c72438d4a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -21,57 +21,6 @@ using namespace clang; using namespace ento; using namespace retaincountchecker; -StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugKind BT) { - switch (BT) { - case UseAfterRelease: - return "Use-after-release"; - case ReleaseNotOwned: - return "Bad release"; - case DeallocNotOwned: - return "-dealloc sent to non-exclusively owned object"; - case FreeNotOwned: - return "freeing non-exclusively owned object"; - case OverAutorelease: - return "Object autoreleased too many times"; - case ReturnNotOwnedForOwned: - return "Method should return an owned object"; - case LeakWithinFunction: - return "Leak"; - case LeakAtReturn: - return "Leak of returned object"; - } - llvm_unreachable("Unknown RefCountBugKind"); -} - -StringRef RefCountBug::getDescription() const { - switch (BT) { - case UseAfterRelease: - return "Reference-counted object is used after it is released"; - case ReleaseNotOwned: - return "Incorrect decrement of the reference count of an object that is " - "not owned at this point by the caller"; - case DeallocNotOwned: - return "-dealloc sent to object that may be referenced elsewhere"; - case FreeNotOwned: - return "'free' called on an object that may be referenced elsewhere"; - case OverAutorelease: - return "Object autoreleased too many times"; - case ReturnNotOwnedForOwned: - return "Object with a +0 retain count returned to caller where a +1 " - "(owning) retain count is expected"; - case LeakWithinFunction: - case LeakAtReturn: - return ""; - } - llvm_unreachable("Unknown RefCountBugKind"); -} - -RefCountBug::RefCountBug(CheckerNameRef Checker, RefCountBugKind BT) - : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount, - /*SuppressOnSink=*/BT == LeakWithinFunction || - BT == LeakAtReturn), - BT(BT) {} - static bool isNumericLiteralExpression(const Expr *E) { // FIXME: This set of cases was copied from SemaExprObjC. return isa(BR.getBugType()); - - bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned || - BT.getBugType() == RefCountBug::DeallocNotOwned; - const SourceManager &SM = BRC.getSourceManager(); CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); if (auto CE = N->getLocationAs()) @@ -490,7 +436,7 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, std::string sbuf; llvm::raw_string_ostream os(sbuf); - if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) { + if (PrevT && IsReleaseUnowned && CurrV.isNotOwned() && PrevT->isOwned()) { os << "Object is now not exclusively owned"; auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM); return std::make_shared(Pos, sbuf); @@ -815,10 +761,8 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, if (K == ObjKind::ObjC || K == ObjKind::CF) { os << "whose name ('" << *FD << "') does not contain 'Copy' or 'Create'. This violates the " - "naming" - " convention rules given in the Memory Management Guide for " - "Core" - " Foundation"; + "naming convention rules given in the Memory Management Guide " + "for Core Foundation"; } else if (RV->getObjKind() == ObjKind::OS) { std::string FuncName = FD->getNameAsString(); os << "whose name ('" << FuncName << "') starts with '" @@ -836,19 +780,20 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, } RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, - ExplodedNode *n, SymbolRef sym, bool isLeak) - : PathSensitiveBugReport(D, D.getDescription(), n), Sym(sym), + ExplodedNode *n, SymbolRef sym, bool isLeak, + bool IsReleaseUnowned) + : PathSensitiveBugReport(D, D.getReportMessage(), n), Sym(sym), isLeak(isLeak) { if (!isLeak) - addVisitor(sym); + addVisitor(sym, IsReleaseUnowned); } RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, StringRef endText) - : PathSensitiveBugReport(D, D.getDescription(), endText, n) { + : PathSensitiveBugReport(D, D.getReportMessage(), endText, n) { - addVisitor(sym); + addVisitor(sym, /*IsReleaseUnowned=*/false); } void RefLeakReport::deriveParamLocation(CheckerContext &Ctx) { diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h index d05900895c6a6..6ceb86fec6433 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h @@ -25,25 +25,44 @@ namespace ento { namespace retaincountchecker { class RefCountBug : public BugType { + StringRef ReportMessage; + public: - enum RefCountBugKind { - UseAfterRelease, - ReleaseNotOwned, - DeallocNotOwned, - FreeNotOwned, - OverAutorelease, - ReturnNotOwnedForOwned, - LeakWithinFunction, - LeakAtReturn, - }; - RefCountBug(CheckerNameRef Checker, RefCountBugKind BT); - StringRef getDescription() const; - - RefCountBugKind getBugType() const { return BT; } - -private: - RefCountBugKind BT; - static StringRef bugTypeToName(RefCountBugKind BT); + RefCountBug(const CheckerFrontend *CF, StringRef Desc, StringRef ReportMsg, + bool SuppressOnSink = false) + : BugType(CF, Desc, categories::MemoryRefCount, SuppressOnSink), + ReportMessage(ReportMsg) {} + StringRef getReportMessage() const { return ReportMessage; } +}; + +class RefCountFrontend : public CheckerFrontend { +public: + const RefCountBug UseAfterRelease{ + this, "Use-after-release", + "Reference-counted object is used after it is released"}; + const RefCountBug ReleaseNotOwned{ + this, "Bad release", + "Incorrect decrement of the reference count of an object that is not " + "owned at this point by the caller"}; + const RefCountBug DeallocNotOwned{ + this, "-dealloc sent to non-exclusively owned object", + "-dealloc sent to object that may be referenced elsewhere"}; + const RefCountBug FreeNotOwned{ + this, "freeing non-exclusively owned object", + "'free' called on an object that may be referenced elsewhere"}; + const RefCountBug OverAutorelease{this, "Object autoreleased too many times", + "Object autoreleased too many times"}; + const RefCountBug ReturnNotOwnedForOwned{ + this, "Method should return an owned object", + "Object with a +0 retain count returned to caller where a +1 (owning) " + "retain count is expected"}; + // For these two bug types the report message will be generated dynamically + // by `RefLeakReport::createDescription` so the empty string taken from the + // BugType will be ignored (overwritten). + const RefCountBug LeakWithinFunction{this, "Leak", /*ReportMsg=*/"", + /*SuppressOnSink=*/true}; + const RefCountBug LeakAtReturn{this, "Leak of returned object", + /*ReportMsg=*/"", /*SuppressOnSink=*/true}; }; class RefCountReport : public PathSensitiveBugReport { @@ -53,8 +72,8 @@ class RefCountReport : public PathSensitiveBugReport { public: RefCountReport(const RefCountBug &D, const LangOptions &LOpts, - ExplodedNode *n, SymbolRef sym, - bool isLeak=false); + ExplodedNode *n, SymbolRef sym, bool isLeak = false, + bool IsReleaseUnowned = false); RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym,