From: Vlad Tsyrklevich Date: Fri, 18 Jan 2019 08:43:22 +0000 (+0000) Subject: Fix failing MSan bots X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0a455fbe13bd9161dfb85788173ad8c0215e5d7b;p=clang Fix failing MSan bots Revert r351508-351514, this block of changes introduced a consistent MSan failure on the sanitizer bots. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@351528 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index f830dc475b..9041f4c1af 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -95,7 +95,7 @@ protected: friend class BugReportEquivClass; friend class BugReporter; - const BugType& BT; + BugType& BT; const Decl *DeclWithIssue = nullptr; std::string ShortDescription; std::string Description; @@ -164,15 +164,15 @@ private: void popInterestingSymbolsAndRegions(); public: - BugReport(const BugType& bt, StringRef desc, const ExplodedNode *errornode) + BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode) : BT(bt), Description(desc), ErrorNode(errornode) {} - BugReport(const BugType& bt, StringRef shortDesc, StringRef desc, + BugReport(BugType& bt, StringRef shortDesc, StringRef desc, const ExplodedNode *errornode) : BT(bt), ShortDescription(shortDesc), Description(desc), ErrorNode(errornode) {} - BugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l) + BugReport(BugType &bt, StringRef desc, PathDiagnosticLocation l) : BT(bt), Description(desc), Location(l) {} /// Create a BugReport with a custom uniqueing location. @@ -190,7 +190,7 @@ public: virtual ~BugReport(); const BugType& getBugType() const { return BT; } - //BugType& getBugType() { return BT; } + BugType& getBugType() { return BT; } /// True when the report has an execution path associated with it. /// @@ -481,7 +481,7 @@ public: return {}; } - void Register(const BugType *BT); + void Register(BugType *BT); /// Add the given report to the set of reports tracked by BugReporter. /// diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h index df1507d85b..727ec7c66a 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h @@ -38,14 +38,12 @@ private: virtual void anchor(); public: - BugType(CheckName Check, StringRef Name, StringRef Cat, - bool SuppressOnSink=false) + BugType(CheckName Check, StringRef Name, StringRef Cat) : Check(Check), Name(Name), Category(Cat), Checker(nullptr), - SuppressOnSink(SuppressOnSink) {} - BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat, - bool SuppressOnSink=false) + SuppressOnSink(false) {} + BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat) : Check(Checker->getCheckName()), Name(Name), Category(Cat), - Checker(Checker), SuppressOnSink(SuppressOnSink) {} + Checker(Checker), SuppressOnSink(false) {} virtual ~BugType() = default; StringRef getName() const { return Name; } @@ -66,6 +64,7 @@ public: /// type should be suppressed if the end node of the report is post-dominated /// by a sink node. bool isSuppressOnSink() const { return SuppressOnSink; } + void setSuppressOnSink(bool x) { SuppressOnSink = x; } }; class BuiltinBug : public BugType { diff --git a/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h b/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h index 4a2e3e7fe9..4fcaa794c1 100644 --- a/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h +++ b/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h @@ -685,10 +685,6 @@ public: Optional canEval(const CallExpr *CE, const FunctionDecl *FD, bool &hasTrustedImplementationAnnotation); - /// \return Whether the type corresponds to a known smart pointer - /// implementation (that is, everything about it is inlineable). - static bool isKnownSmartPointer(QualType QT); - bool isTrustedReferenceCountImplementation(const FunctionDecl *FD); const RetainSummary *getSummary(const CallEvent &Call, diff --git a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp index c0ea95be78..e719e19d68 100644 --- a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -399,14 +399,14 @@ bool isZero(ProgramStateRef State, const NonLoc &Val); IteratorChecker::IteratorChecker() { OutOfRangeBugType.reset( - new BugType(this, "Iterator out of range", "Misuse of STL APIs", - /*SuppressOnSink=*/true)); + new BugType(this, "Iterator out of range", "Misuse of STL APIs")); + OutOfRangeBugType->setSuppressOnSink(true); MismatchedBugType.reset( - new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs", - /*SuppressOnSink=*/true)); + new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs")); + MismatchedBugType->setSuppressOnSink(true); InvalidatedBugType.reset( - new BugType(this, "Iterator invalidated", "Misuse of STL APIs", - /*SuppressOnSink=*/true)); + new BugType(this, "Iterator invalidated", "Misuse of STL APIs")); + InvalidatedBugType->setSuppressOnSink(true); } void IteratorChecker::checkPreCall(const CallEvent &Call, diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 4977dd4fd9..ae1b1fc837 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -2301,14 +2301,14 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, assert(N); if (!BT_Leak[*CheckKind]) { + BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak", + categories::MemoryError)); // Leaks should not be reported if they are post-dominated by a sink: // (1) Sinks are higher importance bugs. // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending // with __noreturn functions such as assert() or exit(). We choose not // to report leaks on such paths. - BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak", - categories::MemoryError, - /*SuppressOnSink=*/true)); + BT_Leak[*CheckKind]->setSuppressOnSink(true); } // Most bug reports are cached at the location where they occurred. diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index cd9273c8a4..8fa2a77741 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -29,10 +29,6 @@ const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) { return State->get(Sym); } -} // end namespace retaincountchecker -} // end namespace ento -} // end namespace clang - static ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym, RefVal Val) { assert(Sym != nullptr); @@ -43,6 +39,73 @@ static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) { return State->remove(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() << " | "; @@ -351,6 +414,20 @@ 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 @@ -452,13 +529,6 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ, C.addTransition(state); } -static bool isSmartPtrField(const MemRegion *MR) { - const auto *TR = dyn_cast( - cast(MR)->getSuperRegion()); - return TR && RetainSummaryManager::isKnownSmartPointer(TR->getValueType()); -} - - /// A value escapes in these possible cases: /// /// - binding to something that is not a memory region. @@ -466,15 +536,10 @@ static bool isSmartPtrField(const MemRegion *MR) { /// - 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, unless it is a known smart pointer -/// implementation, about which we know that it is inlined. +/// assigned to a struct field, so be conservative here and let the symbol go. /// FIXME: This could definitely be improved upon. static bool shouldEscapeRegion(const MemRegion *R) { - if (isSmartPtrField(R)) - return false; - const auto *VR = dyn_cast(R); - if (!R->hasStackStorage() || !VR) return true; @@ -802,23 +867,6 @@ 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, @@ -838,9 +886,30 @@ 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( - errorKindToBugKind(ErrorKind, Sym), - C.getASTContext().getLangOpts(), N, Sym); + *BT, C.getASTContext().getLangOpts(), N, Sym); report->addRange(ErrorRange); C.emitReport(std::move(report)); } @@ -1043,8 +1112,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(leakAtReturn, LOpts, N, Sym, C); + auto R = llvm::make_unique( + *getLeakAtReturnBug(LOpts), LOpts, N, Sym, C); C.emitReport(std::move(R)); } return N; @@ -1068,8 +1137,11 @@ 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( - returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); + *returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); C.emitReport(std::move(R)); } return N; @@ -1221,9 +1293,12 @@ 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(overAutorelease, LOpts, N, Sym, - os.str()); + auto R = llvm::make_unique(*overAutorelease, LOpts, N, Sym, + os.str()); Ctx.emitReport(std::move(R)); } @@ -1269,8 +1344,11 @@ RetainCountChecker::processLeaks(ProgramStateRef state, if (N) { for (SymbolRef L : Leaked) { - const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn; - Ctx.emitReport(llvm::make_unique(BT, LOpts, N, L, Ctx)); + RefCountBug *BT = Pred ? getLeakWithinFunctionBug(LOpts) + : getLeakAtReturnBug(LOpts); + assert(BT && "BugType not initialized."); + + Ctx.emitReport(llvm::make_unique(*BT, LOpts, N, L, Ctx)); } } diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h index ddb4a9e36e..280a672f70 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -251,15 +251,10 @@ class RetainCountChecker check::RegionChanges, 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}; + mutable std::unique_ptr useAfterRelease, releaseNotOwned; + mutable std::unique_ptr deallocNotOwned; + mutable std::unique_ptr overAutorelease, returnNotOwnedForOwned; + mutable std::unique_ptr leakWithinFunction, leakAtReturn; mutable std::unique_ptr Summaries; public: @@ -271,7 +266,11 @@ public: /// Track sublcasses of OSObject. bool TrackOSObjects = false; - RetainCountChecker() {}; + RetainCountChecker() {} + + RefCountBug *getLeakWithinFunctionBug(const LangOptions &LOpts) const; + + RefCountBug *getLeakAtReturnBug(const LangOptions &LOpts) const; RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const { // FIXME: We don't support ARC being turned on and off during one analysis. @@ -337,9 +336,6 @@ public: RefVal V, ArgEffect E, RefVal::Kind &hasErr, CheckerContext &C) const; - const RefCountBug &errorKindToBugKind(RefVal::Kind ErrorKind, - SymbolRef Sym) const; - void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, RefVal::Kind ErrorKind, SymbolRef Sym, CheckerContext &C) const; diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index b7a866d3ff..cda1a928de 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -19,54 +19,6 @@ using namespace clang; using namespace ento; using namespace retaincountchecker; -StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType 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"; - } -} - -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 ""; - } -} - -RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT) - : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount, - /*SupressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn), - BT(BT) {} - static bool isNumericLiteralExpression(const Expr *E) { // FIXME: This set of cases was copied from SemaExprObjC. return isa(E) || @@ -90,8 +42,7 @@ static std::string getPrettyTypeName(QualType QT) { /// Write information about the type state change to {@code os}, /// return whether the note should be generated. static bool shouldGenerateNote(llvm::raw_string_ostream &os, - const RefVal *PrevT, - const RefVal &CurrV, + const RefVal *PrevT, const RefVal &CurrV, bool DeallocSent) { // Get the previous type state. RefVal PrevV = *PrevT; @@ -181,32 +132,6 @@ static Optional findArgIdxOfSymbol(ProgramStateRef CurrSt, return None; } -Optional findMetaClassAlloc(const Expr *Callee) { - if (const auto *ME = dyn_cast(Callee)) { - if (ME->getMemberDecl()->getNameAsString() != "alloc") - return None; - const Expr *This = ME->getBase()->IgnoreParenImpCasts(); - if (const auto *DRE = dyn_cast(This)) { - const ValueDecl *VD = DRE->getDecl(); - if (VD->getNameAsString() != "metaClass") - return None; - - if (const auto *RD = dyn_cast(VD->getDeclContext())) - return RD->getNameAsString(); - - } - } - return None; -} - -std::string findAllocatedObjectName(const Stmt *S, - QualType QT) { - if (const auto *CE = dyn_cast(S)) - if (auto Out = findMetaClassAlloc(CE->getCallee())) - return *Out; - return getPrettyTypeName(QT); -} - static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, const LocationContext *LCtx, const RefVal &CurrV, SymbolRef &Sym, @@ -264,7 +189,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, os << "a Core Foundation object of type '" << Sym->getType().getAsString() << "' with a "; } else if (CurrV.getObjKind() == ObjKind::OS) { - os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType()) + os << "an OSObject of type '" << getPrettyTypeName(Sym->getType()) << "' with a "; } else if (CurrV.getObjKind() == ObjKind::Generalized) { os << "an object of type '" << Sym->getType().getAsString() @@ -413,7 +338,15 @@ annotateConsumedSummaryMismatch(const ExplodedNode *N, if (os.str().empty()) return nullptr; - PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM); + // FIXME: remove the code duplication with NoStoreFuncVisitor. + PathDiagnosticLocation L; + if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { + L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext()); + } else { + L = PathDiagnosticLocation( + Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM); + } + return std::make_shared(L, os.str()); } @@ -421,11 +354,6 @@ std::shared_ptr RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { - const auto &BT = static_cast(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()) @@ -444,8 +372,7 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, const LocationContext *LCtx = N->getLocationContext(); const RefVal* CurrT = getRefBinding(CurrSt, Sym); - if (!CurrT) - return nullptr; + if (!CurrT) return nullptr; const RefVal &CurrV = *CurrT; const RefVal *PrevT = getRefBinding(PrevSt, Sym); @@ -455,12 +382,6 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, std::string sbuf; llvm::raw_string_ostream os(sbuf); - if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) { - os << "Object is now not exclusively owned"; - auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM); - return std::make_shared(Pos, os.str()); - } - // This is the allocation site since the previous node had no bindings // for this symbol. if (!PrevT) { @@ -507,9 +428,9 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, // program point bool DeallocSent = false; - const ProgramPointTag *Tag = N->getLocation().getTag(); - if (Tag && Tag->getTagDescription().contains( - RetainCountChecker::DeallocTagDescription)) { + if (N->getLocation().getTag() && + N->getLocation().getTag()->getTagDescription().contains( + RetainCountChecker::DeallocTagDescription)) { // We only have summaries attached to nodes after evaluating CallExpr and // ObjCMessageExprs. const Stmt *S = N->getLocation().castAs().getStmt(); @@ -764,15 +685,15 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, return std::make_shared(L, os.str()); } -RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, +RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, - bool isLeak) - : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) { - if (!isLeak) + bool registerVisitor) + : BugReport(D, D.getDescription(), n), Sym(sym) { + if (registerVisitor) addVisitor(llvm::make_unique(sym)); } -RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, +RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, StringRef endText) : BugReport(D, D.getDescription(), endText, n) { @@ -858,10 +779,10 @@ void RefLeakReport::createDescription(CheckerContext &Ctx) { } } -RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, +RefLeakReport::RefLeakReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx) - : RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) { + : RefCountReport(D, LOpts, n, sym, false) { deriveAllocLocation(Ctx, sym); if (!AllocBinding) diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h index f6e4ded960..9f796abe8e 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h @@ -25,44 +25,32 @@ namespace ento { namespace retaincountchecker { class RefCountBug : public BugType { +protected: + RefCountBug(const CheckerBase *checker, StringRef name) + : BugType(checker, name, categories::MemoryRefCount) {} + public: - enum RefCountBugType { - UseAfterRelease, - ReleaseNotOwned, - DeallocNotOwned, - FreeNotOwned, - OverAutorelease, - ReturnNotOwnedForOwned, - LeakWithinFunction, - LeakAtReturn, - }; - RefCountBug(const CheckerBase *checker, RefCountBugType BT); - StringRef getDescription() const; - RefCountBugType getBugType() const { - return BT; - } + virtual const char *getDescription() const = 0; -private: - RefCountBugType BT; - static StringRef bugTypeToName(RefCountBugType BT); + virtual bool isLeak() const { return false; } }; class RefCountReport : public BugReport { protected: SymbolRef Sym; - bool isLeak; public: - RefCountReport(const RefCountBug &D, const LangOptions &LOpts, + RefCountReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, - bool isLeak=false); + bool registerVisitor = true); - RefCountReport(const RefCountBug &D, const LangOptions &LOpts, + RefCountReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, StringRef endText); llvm::iterator_range getRanges() override { - if (!isLeak) + const RefCountBug& BugTy = static_cast(getBugType()); + if (!BugTy.isLeak()) return BugReport::getRanges(); return llvm::make_range(ranges_iterator(), ranges_iterator()); } @@ -81,7 +69,7 @@ class RefLeakReport : public RefCountReport { void createDescription(CheckerContext &Ctx); public: - RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, + RefLeakReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx); PathDiagnosticLocation getLocation(const SourceManager &SM) const override { diff --git a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 46b584c726..819d437e68 100644 --- a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -109,10 +109,10 @@ SimpleStreamChecker::SimpleStreamChecker() DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); - // Sinks are higher importance bugs as well as calls to assert() or exit(0). LeakBugType.reset( - new BugType(this, "Resource Leak", "Unix Stream API Error", - /*SuppressOnSink=*/true)); + new BugType(this, "Resource Leak", "Unix Stream API Error")); + // Sinks are higher importance bugs as well as calls to assert() or exit(0). + LeakBugType->setSuppressOnSink(true); } void SimpleStreamChecker::checkPostCall(const CallEvent &Call, diff --git a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp index 393758a9ab..748b226b7a 100644 --- a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -276,8 +276,8 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, new BugType(CheckNames[CK_Unterminated].getName().empty() ? CheckNames[CK_Uninitialized] : CheckNames[CK_Unterminated], - "Leaked va_list", categories::MemoryError, - /*SuppressOnSink=*/true)); + "Leaked va_list", categories::MemoryError)); + BT_leakedvalist->setSuppressOnSink(true); } const ExplodedNode *StartNode = getStartCallSite(N, Reg); diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index c00bdcbacd..fd7f532104 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1247,7 +1247,7 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, static std::unique_ptr generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) { - const BugType &BT = R->getBugType(); + BugType &BT = R->getBugType(); return llvm::make_unique( R->getBugType().getCheckName(), R->getDeclWithIssue(), R->getBugType().getName(), R->getDescription(), @@ -2684,7 +2684,7 @@ GRBugReporter::generatePathDiagnostics( return Out; } -void BugReporter::Register(const BugType *BT) { +void BugReporter::Register(BugType *BT) { BugTypes = F.add(BugTypes, BT); } @@ -2718,7 +2718,7 @@ void BugReporter::emitReport(std::unique_ptr R) { R->Profile(ID); // Lookup the equivance class. If there isn't one, create it. - const BugType& BT = R->getBugType(); + BugType& BT = R->getBugType(); Register(&BT); void *InsertPos; BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos); @@ -2836,7 +2836,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, SmallVectorImpl &bugReports) { BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); assert(I != E); - const BugType& BT = I->getBugType(); + BugType& BT = I->getBugType(); // If we don't need to suppress any of the nodes because they are // post-dominated by a sink, simply add all the nodes in the equivalence class diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 1e9ec563a8..da94b6eb21 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -308,8 +308,9 @@ public: if (RegionOfInterest->isSubRegionOf(SelfRegion) && potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(), IvarR->getDecl())) - return notModifiedDiagnostics(N, {}, SelfRegion, "self", - /*FirstIsReferenceType=*/false, 1); + return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, SelfRegion, + "self", /*FirstIsReferenceType=*/false, + 1); } } @@ -317,7 +318,8 @@ public: const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion(); if (RegionOfInterest->isSubRegionOf(ThisR) && !CCall->getDecl()->isImplicit()) - return notModifiedDiagnostics(N, {}, ThisR, "this", + return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, ThisR, + "this", /*FirstIsReferenceType=*/false, 1); // Do not generate diagnostics for not modified parameters in @@ -336,17 +338,18 @@ public: QualType T = PVD->getType(); while (const MemRegion *R = S.getAsRegion()) { if (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T)) - return notModifiedDiagnostics(N, {}, R, ParamName, - ParamIsReferenceType, IndirectionLevel); + return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, R, + ParamName, ParamIsReferenceType, + IndirectionLevel); QualType PT = T->getPointeeType(); if (PT.isNull() || PT->isVoidType()) break; if (const RecordDecl *RD = PT->getAsRecordDecl()) if (auto P = findRegionOfInterestInRecord(RD, State, R)) - return notModifiedDiagnostics(N, *P, RegionOfInterest, ParamName, - ParamIsReferenceType, - IndirectionLevel); + return notModifiedDiagnostics( + Ctx, *CallExitLoc, Call, *P, RegionOfInterest, ParamName, + ParamIsReferenceType, IndirectionLevel); S = State->getSVal(R, PT); T = PT; @@ -520,12 +523,19 @@ private: /// \return Diagnostics piece for region not modified in the current function. std::shared_ptr - notModifiedDiagnostics(const ExplodedNode *N, const RegionVector &FieldChain, + notModifiedDiagnostics(const LocationContext *Ctx, CallExitBegin &CallExitLoc, + CallEventRef<> Call, const RegionVector &FieldChain, const MemRegion *MatchedRegion, StringRef FirstElement, bool FirstIsReferenceType, unsigned IndirectionLevel) { - PathDiagnosticLocation L = - PathDiagnosticLocation::create(N->getLocation(), SM); + PathDiagnosticLocation L; + if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { + L = PathDiagnosticLocation::createBegin(RS, SM, Ctx); + } else { + L = PathDiagnosticLocation( + Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), + SM); + } SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 005bae097c..3e93bb6a7c 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -735,12 +735,6 @@ PathDiagnosticLocation::create(const ProgramPoint& P, return getLocationForCaller(CEE->getCalleeContext(), CEE->getLocationContext(), SMng); - } else if (auto CEB = P.getAs()) { - if (const ReturnStmt *RS = CEB->getReturnStmt()) - return PathDiagnosticLocation::createBegin(RS, SMng, - CEB->getLocationContext()); - return PathDiagnosticLocation( - CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng); } else if (Optional BE = P.getAs()) { CFGElement BlockFront = BE->getBlock()->front(); if (auto StmtElt = BlockFront.getAs()) { diff --git a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp index 2eb422f632..2e40cc3338 100644 --- a/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp +++ b/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp @@ -146,7 +146,7 @@ static bool isSubclass(const Decl *D, } static bool isOSObjectSubclass(const Decl *D) { - return isSubclass(D, "OSMetaClassBase"); + return isSubclass(D, "OSObject"); } static bool isOSObjectDynamicCast(StringRef S) { @@ -199,20 +199,6 @@ static bool isOSObjectRelated(const CXXMethodDecl *MD) { return false; } -bool -RetainSummaryManager::isKnownSmartPointer(QualType QT) { - QT = QT.getCanonicalType(); - const auto *RD = QT->getAsCXXRecordDecl(); - if (!RD) - return false; - const IdentifierInfo *II = RD->getIdentifier(); - if (II && II->getName() == "smart_ptr") - if (const auto *ND = dyn_cast(RD->getDeclContext())) - if (ND->getNameAsString() == "os") - return true; - return false; -} - const RetainSummary * RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD, StringRef FName, QualType RetTy) { diff --git a/test/Analysis/os_object_base.h b/test/Analysis/os_object_base.h deleted file mode 100644 index e388dddd58..0000000000 --- a/test/Analysis/os_object_base.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef _OS_BASE_H -#define _OS_BASE_H - -#define OS_CONSUME __attribute__((os_consumed)) -#define OS_RETURNS_RETAINED __attribute__((os_returns_retained)) -#define OS_RETURNS_RETAINED_ON_ZERO __attribute__((os_returns_retained_on_zero)) -#define OS_RETURNS_RETAINED_ON_NONZERO __attribute__((os_returns_retained_on_non_zero)) -#define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained)) -#define OS_CONSUMES_THIS __attribute__((os_consumes_this)) - -#define OSTypeID(type) (type::metaClass) - -#define OSDynamicCast(type, inst) \ - ((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type))) - -#define OSTypeAlloc(type) ((type *) ((type::metaClass)->alloc())) - -using size_t = decltype(sizeof(int)); - -struct OSMetaClass; - -struct OSMetaClassBase { - static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst, - const OSMetaClass *meta); - - virtual void retain() const; - virtual void release() const; - virtual void free(); - virtual ~OSMetaClassBase(){}; -}; - -struct OSObject : public OSMetaClassBase { - virtual ~OSObject(){} - - unsigned int foo() { return 42; } - - virtual OS_RETURNS_NOT_RETAINED OSObject *identity(); - - static OSObject *generateObject(int); - - static OSObject *getObject(); - static OSObject *GetObject(); - - static void * operator new(size_t size); - - static const OSMetaClass * const metaClass; -}; - -struct OSMetaClass : public OSMetaClassBase { - virtual OSObject * alloc() const; - virtual ~OSMetaClass(){} -}; - -#endif /* _OS_BASE_H */ diff --git a/test/Analysis/os_smart_ptr.h b/test/Analysis/os_smart_ptr.h deleted file mode 100644 index 8faf294f75..0000000000 --- a/test/Analysis/os_smart_ptr.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef _OS_SMART_POINTER_H -#define _OS_SMART_POINTER_H - -#include "os_object_base.h" - -namespace os { - -template -struct smart_ptr { - smart_ptr() : pointer(nullptr) {} - - explicit smart_ptr(T *&p) : pointer(p) { - if (pointer) { - _retain(pointer); - } - } - - smart_ptr(smart_ptr const &rhs) : pointer(rhs.pointer) { - if (pointer) { - _retain(pointer); - } - } - - smart_ptr & operator=(T *&rhs) { - smart_ptr(rhs).swap(*this); - return *this; - } - - smart_ptr & operator=(smart_ptr &rhs) { - smart_ptr(rhs).swap(*this); - return *this; - } - - ~smart_ptr() { - if (pointer) { - _release(pointer); - } - } - - void reset() { - smart_ptr().swap(*this); - } - - T *get() const { - return pointer; - } - - T ** get_for_out_param() { - reset(); - return &pointer; - } - - T * operator->() const { - OSPTR_LOG("Dereference smart_ptr with %p\n", pointer); - return pointer; - } - - explicit - operator bool() const { - return pointer != nullptr; - } - - inline void - swap(smart_ptr &p) { - T *temp = pointer; - pointer = p.pointer; - p.pointer = temp; - } - - static inline void - _retain(T *obj) { - obj->retain(); - } - - static inline void - _release(T *obj) { - obj->release(); - } - - static inline T * - _alloc() { - return new T; - } - - T *pointer; -}; -} - -#endif /* _OS_SMART_POINTER_H */ diff --git a/test/Analysis/osobject-retain-release.cpp b/test/Analysis/osobject-retain-release.cpp index 9bc2e20454..1e44866eff 100644 --- a/test/Analysis/osobject-retain-release.cpp +++ b/test/Analysis/osobject-retain-release.cpp @@ -1,10 +1,44 @@ // RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-output=text\ // RUN: -analyzer-checker=core,osx -verify %s -#include "os_object_base.h" -#include "os_smart_ptr.h" +struct OSMetaClass; + +#define OS_CONSUME __attribute__((os_consumed)) +#define OS_RETURNS_RETAINED __attribute__((os_returns_retained)) +#define OS_RETURNS_RETAINED_ON_ZERO __attribute__((os_returns_retained_on_zero)) +#define OS_RETURNS_RETAINED_ON_NONZERO __attribute__((os_returns_retained_on_non_zero)) +#define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained)) +#define OS_CONSUMES_THIS __attribute__((os_consumes_this)) + +#define OSTypeID(type) (type::metaClass) + +#define OSDynamicCast(type, inst) \ + ((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type))) + +using size_t = decltype(sizeof(int)); + +struct OSObject { + virtual void retain(); + virtual void release() {}; + virtual void free(); + virtual ~OSObject(){} + + unsigned int foo() { return 42; } + + virtual OS_RETURNS_NOT_RETAINED OSObject *identity(); + + static OSObject *generateObject(int); + + static OSObject *getObject(); + static OSObject *GetObject(); + + static void * operator new(size_t size); + + static const OSMetaClass * const metaClass; +}; struct OSIterator : public OSObject { + static const OSMetaClass * const metaClass; }; @@ -54,6 +88,9 @@ struct OtherStruct { OtherStruct(OSArray *arr); }; +struct OSMetaClassBase { + static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta); +}; void escape(void *); void escape_with_source(void *p) {} @@ -579,68 +616,3 @@ typedef bool (^Blk)(OSObject *); void test_escape_to_unknown_block(Blk blk) { blk(getObject()); // no-crash } - -using OSObjectPtr = os::smart_ptr; - -void test_smart_ptr_uaf() { - OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} - { - OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr'}} - // expected-note@-1{{Returning from constructor for 'smart_ptr'}} - // expected-note@os_smart_ptr.h:13{{Taking true branch}} - // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}} - // expected-note@os_smart_ptr.h:72{{Reference count incremented. The object now has a +2 retain count}} - // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}} - } // expected-note{{Calling '~smart_ptr'}} - // expected-note@os_smart_ptr.h:35{{Taking true branch}} - // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}} - // expected-note@os_smart_ptr.h:77{{Reference count decremented. The object now has a +1 retain count}} - // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}} - // expected-note@-5{{Returning from '~smart_ptr'}} - obj->release(); // expected-note{{Object released}} - obj->release(); // expected-warning{{Reference-counted object is used after it is released}} -// expected-note@-1{{Reference-counted object is used after it is released}} -} - -void test_smart_ptr_leak() { - OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} - { - OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr'}} - // expected-note@-1{{Returning from constructor for 'smart_ptr'}} - // expected-note@os_smart_ptr.h:13{{Taking true branch}} - // expected-note@os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}} - // expected-note@os_smart_ptr.h:72{{Reference count incremented. The object now has a +2 retain count}} - // expected-note@os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}} - } // expected-note{{Calling '~smart_ptr'}} - // expected-note@os_smart_ptr.h:35{{Taking true branch}} - // expected-note@os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}} - // expected-note@os_smart_ptr.h:77{{Reference count decremented. The object now has a +1 retain count}} - // expected-note@os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}} - // expected-note@-5{{Returning from '~smart_ptr'}} -} // expected-warning{{Potential leak of an object stored into 'obj'}} -// expected-note@-1{{Object leaked: object allocated and stored into 'obj' is not referenced later in this execution path and has a retain count of +1}} - -void test_smart_ptr_no_leak() { - OSObject *obj = new OSObject; - { - OSObjectPtr p(obj); - } - obj->release(); -} - -void test_ostypealloc_correct_diagnostic_name() { - OSArray *arr = OSTypeAlloc(OSArray); // expected-note{{Call to method 'OSMetaClass::alloc' returns an OSObject of type 'OSArray' with a +1 retain count}} - arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}} - arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}} -} // expected-note{{Object leaked: object allocated and stored into 'arr' is not referenced later in this execution path and has a retain count of +1}} - // expected-warning@-1{{Potential leak of an object stored into 'arr'}} - -void escape_elsewhere(OSObject *obj); - -void test_free_on_escaped_object_diagnostics() { - OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}} - escape_elsewhere(obj); // expected-note{{Object is now not exclusively owned}} - obj->free(); // expected-note{{'free' called on an object that may be referenced elsewhere}} - // expected-warning@-1{{'free' called on an object that may be referenced elsewhere}} -} - diff --git a/test/Analysis/test-separate-retaincount.cpp b/test/Analysis/test-separate-retaincount.cpp index 0835c88cf9..be6534f544 100644 --- a/test/Analysis/test-separate-retaincount.cpp +++ b/test/Analysis/test-separate-retaincount.cpp @@ -2,8 +2,6 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-disable-checker osx.OSObjectRetainCount -DNO_OS_OBJECT -verify %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-config "osx.cocoa.RetainCount:CheckOSObject=false" -DNO_OS_OBJECT -verify %s -#include "os_object_base.h" - typedef const void * CFTypeRef; extern CFTypeRef CFRetain(CFTypeRef cf); extern void CFRelease(CFTypeRef cf); @@ -13,6 +11,14 @@ extern CFTypeRef CFCreate() CF_RETURNS_RETAINED; using size_t = decltype(sizeof(int)); +struct OSObject { + virtual void retain(); + virtual void release(); + + static void * operator new(size_t size); + virtual ~OSObject(){} +}; + void cf_overrelease() { CFTypeRef cf = CFCreate(); CFRelease(cf);