diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp | 164 |
1 files changed, 43 insertions, 121 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index 8fa2a777414..cd9273c8a47 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -29,6 +29,10 @@ const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) { return State->get<RefBindings>(Sym); } +} // end namespace retaincountchecker +} // end namespace ento +} // end namespace clang + static ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym, RefVal Val) { assert(Sym != nullptr); @@ -39,73 +43,6 @@ static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) { return State->remove<RefBindings>(Sym); } -class UseAfterRelease : public RefCountBug { -public: - UseAfterRelease(const CheckerBase *checker) - : RefCountBug(checker, "Use-after-release") {} - - const char *getDescription() const override { - return "Reference-counted object is used after it is released"; - } -}; - -class BadRelease : public RefCountBug { -public: - BadRelease(const CheckerBase *checker) : RefCountBug(checker, "Bad release") {} - - const char *getDescription() const override { - return "Incorrect decrement of the reference count of an object that is " - "not owned at this point by the caller"; - } -}; - -class DeallocNotOwned : public RefCountBug { -public: - DeallocNotOwned(const CheckerBase *checker) - : RefCountBug(checker, "-dealloc sent to non-exclusively owned object") {} - - const char *getDescription() const override { - return "-dealloc sent to object that may be referenced elsewhere"; - } -}; - -class OverAutorelease : public RefCountBug { -public: - OverAutorelease(const CheckerBase *checker) - : RefCountBug(checker, "Object autoreleased too many times") {} - - const char *getDescription() const override { - return "Object autoreleased too many times"; - } -}; - -class ReturnedNotOwnedForOwned : public RefCountBug { -public: - ReturnedNotOwnedForOwned(const CheckerBase *checker) - : RefCountBug(checker, "Method should return an owned object") {} - - const char *getDescription() const override { - return "Object with a +0 retain count returned to caller where a +1 " - "(owning) retain count is expected"; - } -}; - -class Leak : public RefCountBug { -public: - Leak(const CheckerBase *checker, StringRef name) : RefCountBug(checker, name) { - // Leaks should not be reported if they are post-dominated by a sink. - setSuppressOnSink(true); - } - - const char *getDescription() const override { return ""; } - - bool isLeak() const override { return true; } -}; - -} // end namespace retaincountchecker -} // end namespace ento -} // end namespace clang - void RefVal::print(raw_ostream &Out) const { if (!T.isNull()) Out << "Tracked " << T.getAsString() << " | "; @@ -414,20 +351,6 @@ void RetainCountChecker::checkPostCall(const CallEvent &Call, checkSummary(*Summ, Call, C); } -RefCountBug * -RetainCountChecker::getLeakWithinFunctionBug(const LangOptions &LOpts) const { - if (!leakWithinFunction) - leakWithinFunction.reset(new Leak(this, "Leak")); - return leakWithinFunction.get(); -} - -RefCountBug * -RetainCountChecker::getLeakAtReturnBug(const LangOptions &LOpts) const { - if (!leakAtReturn) - leakAtReturn.reset(new Leak(this, "Leak of returned object")); - return leakAtReturn.get(); -} - /// GetReturnType - Used to get the return type of a message expression or /// function call with the intention of affixing that type to a tracked symbol. /// While the return type can be queried directly from RetEx, when @@ -529,6 +452,13 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ, C.addTransition(state); } +static bool isSmartPtrField(const MemRegion *MR) { + const auto *TR = dyn_cast<TypedValueRegion>( + cast<SubRegion>(MR)->getSuperRegion()); + return TR && RetainSummaryManager::isKnownSmartPointer(TR->getValueType()); +} + + /// A value escapes in these possible cases: /// /// - binding to something that is not a memory region. @@ -536,10 +466,15 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ, /// - binding to a variable that has a destructor attached using CleanupAttr /// /// We do not currently model what happens when a symbol is -/// assigned to a struct field, so be conservative here and let the symbol go. +/// assigned to a struct field, unless it is a known smart pointer +/// implementation, about which we know that it is inlined. /// FIXME: This could definitely be improved upon. static bool shouldEscapeRegion(const MemRegion *R) { + if (isSmartPtrField(R)) + return false; + const auto *VR = dyn_cast<VarRegion>(R); + if (!R->hasStackStorage() || !VR) return true; @@ -867,6 +802,23 @@ ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state, return setRefBinding(state, sym, V); } +const RefCountBug & +RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind, + SymbolRef Sym) const { + switch (ErrorKind) { + case RefVal::ErrorUseAfterRelease: + return useAfterRelease; + case RefVal::ErrorReleaseNotOwned: + return releaseNotOwned; + case RefVal::ErrorDeallocNotOwned: + if (Sym->getType()->getPointeeCXXRecordDecl()) + return freeNotOwned; + return deallocNotOwned; + default: + llvm_unreachable("Unhandled error."); + } +} + void RetainCountChecker::processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, @@ -886,30 +838,9 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St, if (!N) return; - RefCountBug *BT; - switch (ErrorKind) { - default: - llvm_unreachable("Unhandled error."); - case RefVal::ErrorUseAfterRelease: - if (!useAfterRelease) - useAfterRelease.reset(new UseAfterRelease(this)); - BT = useAfterRelease.get(); - break; - case RefVal::ErrorReleaseNotOwned: - if (!releaseNotOwned) - releaseNotOwned.reset(new BadRelease(this)); - BT = releaseNotOwned.get(); - break; - case RefVal::ErrorDeallocNotOwned: - if (!deallocNotOwned) - deallocNotOwned.reset(new DeallocNotOwned(this)); - BT = deallocNotOwned.get(); - break; - } - - assert(BT); auto report = llvm::make_unique<RefCountReport>( - *BT, C.getASTContext().getLangOpts(), N, Sym); + errorKindToBugKind(ErrorKind, Sym), + C.getASTContext().getLangOpts(), N, Sym); report->addRange(ErrorRange); C.emitReport(std::move(report)); } @@ -1112,8 +1043,8 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag); if (N) { const LangOptions &LOpts = C.getASTContext().getLangOpts(); - auto R = llvm::make_unique<RefLeakReport>( - *getLeakAtReturnBug(LOpts), LOpts, N, Sym, C); + auto R = + llvm::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C); C.emitReport(std::move(R)); } return N; @@ -1137,11 +1068,8 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); if (N) { - if (!returnNotOwnedForOwned) - returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this)); - auto R = llvm::make_unique<RefCountReport>( - *returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); + returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); C.emitReport(std::move(R)); } return N; @@ -1293,12 +1221,9 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, os << "but "; os << "has a +" << V.getCount() << " retain count"; - if (!overAutorelease) - overAutorelease.reset(new OverAutorelease(this)); - const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); - auto R = llvm::make_unique<RefCountReport>(*overAutorelease, LOpts, N, Sym, - os.str()); + auto R = llvm::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym, + os.str()); Ctx.emitReport(std::move(R)); } @@ -1344,11 +1269,8 @@ RetainCountChecker::processLeaks(ProgramStateRef state, if (N) { for (SymbolRef L : Leaked) { - RefCountBug *BT = Pred ? getLeakWithinFunctionBug(LOpts) - : getLeakAtReturnBug(LOpts); - assert(BT && "BugType not initialized."); - - Ctx.emitReport(llvm::make_unique<RefLeakReport>(*BT, LOpts, N, L, Ctx)); + const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn; + Ctx.emitReport(llvm::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx)); } } |