summaryrefslogtreecommitdiffstats
path: root/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp123
1 files changed, 101 insertions, 22 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
index cda1a928de1..b7a866d3ff8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -19,6 +19,54 @@ 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<IntegerLiteral>(E) ||
@@ -42,7 +90,8 @@ 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;
@@ -132,6 +181,32 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
return None;
}
+Optional<std::string> findMetaClassAlloc(const Expr *Callee) {
+ if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
+ if (ME->getMemberDecl()->getNameAsString() != "alloc")
+ return None;
+ const Expr *This = ME->getBase()->IgnoreParenImpCasts();
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
+ const ValueDecl *VD = DRE->getDecl();
+ if (VD->getNameAsString() != "metaClass")
+ return None;
+
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
+ return RD->getNameAsString();
+
+ }
+ }
+ return None;
+}
+
+std::string findAllocatedObjectName(const Stmt *S,
+ QualType QT) {
+ if (const auto *CE = dyn_cast<CallExpr>(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,
@@ -189,7 +264,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 '" << getPrettyTypeName(Sym->getType())
+ os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
<< "' with a ";
} else if (CurrV.getObjKind() == ObjKind::Generalized) {
os << "an object of type '" << Sym->getType().getAsString()
@@ -338,15 +413,7 @@ annotateConsumedSummaryMismatch(const ExplodedNode *N,
if (os.str().empty())
return nullptr;
- // 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);
- }
-
+ PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM);
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
@@ -354,6 +421,11 @@ std::shared_ptr<PathDiagnosticPiece>
RefCountReportVisitor::VisitNode(const ExplodedNode *N,
BugReporterContext &BRC, BugReport &BR) {
+ const auto &BT = static_cast<const RefCountBug&>(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<CallExitBegin>())
@@ -372,7 +444,8 @@ 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);
@@ -382,6 +455,12 @@ 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<PathDiagnosticEventPiece>(Pos, os.str());
+ }
+
// This is the allocation site since the previous node had no bindings
// for this symbol.
if (!PrevT) {
@@ -428,9 +507,9 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N,
// program point
bool DeallocSent = false;
- if (N->getLocation().getTag() &&
- N->getLocation().getTag()->getTagDescription().contains(
- RetainCountChecker::DeallocTagDescription)) {
+ const ProgramPointTag *Tag = N->getLocation().getTag();
+ if (Tag && Tag->getTagDescription().contains(
+ RetainCountChecker::DeallocTagDescription)) {
// We only have summaries attached to nodes after evaluating CallExpr and
// ObjCMessageExprs.
const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
@@ -685,15 +764,15 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
-RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
+RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
ExplodedNode *n, SymbolRef sym,
- bool registerVisitor)
- : BugReport(D, D.getDescription(), n), Sym(sym) {
- if (registerVisitor)
+ bool isLeak)
+ : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) {
+ if (!isLeak)
addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
}
-RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
+RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
ExplodedNode *n, SymbolRef sym,
StringRef endText)
: BugReport(D, D.getDescription(), endText, n) {
@@ -779,10 +858,10 @@ void RefLeakReport::createDescription(CheckerContext &Ctx) {
}
}
-RefLeakReport::RefLeakReport(RefCountBug &D, const LangOptions &LOpts,
+RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,
ExplodedNode *n, SymbolRef sym,
CheckerContext &Ctx)
- : RefCountReport(D, LOpts, n, sym, false) {
+ : RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) {
deriveAllocLocation(Ctx, sym);
if (!AllocBinding)
OpenPOWER on IntegriCloud