aboutsummaryrefslogtreecommitdiff
path: root/lib/Analysis/CFRefCount.cpp
diff options
context:
space:
mode:
authorTed Kremenek <kremenek@apple.com>2009-02-18 03:48:14 +0000
committerTed Kremenek <kremenek@apple.com>2009-02-18 03:48:14 +0000
commitfe9e543a2a363df7fcaa899367d3b2580b63b27c (patch)
treed6a9cc6935655adbccddcac142da70264be5706e /lib/Analysis/CFRefCount.cpp
parent3e030b4c04546229c81449fde66f71b23b3fc550 (diff)
Hooked up the necessary machinery to allow the retain/release checker reference
back to the summary used when evaluating the statement associated with a simulation node. This is now being used to help improve the checker's diagnostics. To get things started, the checker now emits a path diagnostic indicating that 'autorelease' is a no-op in GC mode. Some of these changes are exposing further grossness in the interface between BugReporter and the ExplodedGraph::Trim facilities. These really need to be cleaned up one day. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64881 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Analysis/CFRefCount.cpp')
-rw-r--r--lib/Analysis/CFRefCount.cpp178
1 files changed, 125 insertions, 53 deletions
diff --git a/lib/Analysis/CFRefCount.cpp b/lib/Analysis/CFRefCount.cpp
index 8fea99b4f6..7bb6e7ce1c 100644
--- a/lib/Analysis/CFRefCount.cpp
+++ b/lib/Analysis/CFRefCount.cpp
@@ -1283,8 +1283,11 @@ public:
};
private:
+ typedef llvm::DenseMap<const GRExprEngine::NodeTy*, const RetainSummary*>
+ SummaryLogTy;
+
RetainSummaryManager Summaries;
- llvm::DenseMap<const GRExprEngine::NodeTy*, const RetainSummary*> SummaryLog;
+ SummaryLogTy SummaryLog;
const LangOptions& LOpts;
BugType *useAfterRelease, *releaseNotOwned;
@@ -1332,6 +1335,11 @@ public:
bool isGCEnabled() const { return Summaries.isGCEnabled(); }
const LangOptions& getLangOptions() const { return LOpts; }
+ const RetainSummary *getSummaryOfNode(const ExplodedNode<GRState> *N) const {
+ SummaryLogTy::const_iterator I = SummaryLog.find(N);
+ return I == SummaryLog.end() ? 0 : I->second;
+ }
+
// Calls.
void EvalSummary(ExplodedNodeSet<GRState>& Dst,
@@ -2087,9 +2095,11 @@ namespace {
class VISIBILITY_HIDDEN CFRefReport : public RangedBugReport {
protected:
SymbolRef Sym;
+ const CFRefCount &TF;
public:
- CFRefReport(CFRefBug& D, ExplodedNode<GRState> *n, SymbolRef sym)
- : RangedBugReport(D, D.getDescription(), n), Sym(sym) {}
+ CFRefReport(CFRefBug& D, const CFRefCount &tf,
+ ExplodedNode<GRState> *n, SymbolRef sym)
+ : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {}
virtual ~CFRefReport() {}
@@ -2119,14 +2129,16 @@ namespace {
PathDiagnosticPiece* VisitNode(const ExplodedNode<GRState>* N,
const ExplodedNode<GRState>* PrevN,
const ExplodedGraph<GRState>& G,
- BugReporter& BR);
+ BugReporter& BR,
+ NodeResolver& NR);
};
class VISIBILITY_HIDDEN CFRefLeakReport : public CFRefReport {
SourceLocation AllocSite;
const MemRegion* AllocBinding;
public:
- CFRefLeakReport(CFRefBug& D, ExplodedNode<GRState> *n, SymbolRef sym,
+ CFRefLeakReport(CFRefBug& D, const CFRefCount &tf,
+ ExplodedNode<GRState> *n, SymbolRef sym,
GRExprEngine& Eng);
PathDiagnosticPiece* getEndPath(BugReporter& BR,
@@ -2215,7 +2227,8 @@ std::pair<const char**,const char**> CFRefReport::getExtraDescriptiveText() {
PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode<GRState>* N,
const ExplodedNode<GRState>* PrevN,
const ExplodedGraph<GRState>& G,
- BugReporter& BR) {
+ BugReporter& BR,
+ NodeResolver& NR) {
// Check if the type state has changed.
GRStateManager &StMgr = cast<GRBugReporter>(BR).getStateManager();
@@ -2228,6 +2241,8 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode<GRState>* N,
const RefVal& CurrV = *CurrT;
const RefVal* PrevT = PrevSt.get<RefBindings>(Sym);
+ // This is the allocation site since the previous node had no bindings
+ // for this symbol.
if (!PrevT) {
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
@@ -2277,56 +2292,112 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode<GRState>* N,
return P;
}
-
- // Determine if the typestate has changed.
- RefVal PrevV = *PrevT;
-
- if (PrevV == CurrV)
- return NULL;
-
- // The typestate has changed.
+
+ // Create a string buffer to constain all the useful things we want
+ // to tell the user.
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
- switch (CurrV.getKind()) {
- case RefVal::Owned:
- case RefVal::NotOwned:
-
- if (PrevV.getCount() == CurrV.getCount())
- return 0;
-
- if (PrevV.getCount() > CurrV.getCount())
- os << "Reference count decremented.";
- else
- os << "Reference count incremented.";
+ // Consult the summary to see if there is something special we
+ // should tell the user.
+ if (const RetainSummary *Summ = TF.getSummaryOfNode(NR.getOriginalNode(N))) {
+ // We only have summaries attached to nodes after evaluating CallExpr and
+ // ObjCMessageExprs.
+ Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
+
+ llvm::SmallVector<ArgEffect, 2> AEffects;
+
+ if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
+ // Iterate through the parameter expressions and see if the symbol
+ // was ever passed as an argument.
+ unsigned i = 0;
- if (unsigned Count = CurrV.getCount()) {
- os << " Object has +" << Count;
+ for (CallExpr::arg_iterator AI=CE->arg_begin(), AE=CE->arg_end();
+ AI!=AE; ++AI, ++i) {
+
+ // Retrieve the value of the arugment.
+ SVal X = CurrSt.GetSVal(*AI);
+
+ // Is it the symbol we're interested in?
+ if (!isa<loc::SymbolVal>(X) ||
+ Sym != cast<loc::SymbolVal>(X).getSymbol())
+ continue;
- if (Count > 1)
- os << " retain counts.";
- else
- os << " retain count.";
+ // We have an argument. Get the effect!
+ AEffects.push_back(Summ->getArg(i));
}
+ }
+ else if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
+ if (Expr *receiver = ME->getReceiver()) {
+ SVal RetV = CurrSt.GetSVal(receiver);
+ if (isa<loc::SymbolVal>(RetV) &&
+ Sym == cast<loc::SymbolVal>(RetV).getSymbol()) {
+ // The symbol we are tracking is the receiver.
+ AEffects.push_back(Summ->getReceiverEffect());
+ }
+ }
+ }
- break;
-
- case RefVal::Released:
- os << "Object released.";
- break;
-
- case RefVal::ReturnedOwned:
- os << "Object returned to caller as an owning reference (single retain "
- "count transferred to caller).";
- break;
-
- case RefVal::ReturnedNotOwned:
- os << "Object returned to caller with a +0 (non-owning) retain count.";
- break;
-
- default:
- return NULL;
+ // Emit diagnostics for the argument effects (if any).
+ // FIXME: The typestate logic below should also be folded into
+ // this block.
+ for (llvm::SmallVectorImpl<ArgEffect>::iterator I=AEffects.begin(),
+ E=AEffects.end(); I != E; ++I) {
+
+ // Did we do an 'autorelease' in GC mode?
+ if (TF.isGCEnabled() && *I == Autorelease) {
+ os << "In GC mode an 'autorelease' has no effect.";
+ continue;
+ }
+ }
}
+
+ // Determine if the typestate has changed.
+ RefVal PrevV = *PrevT;
+
+ if (!(PrevV == CurrV)) // The typestate has changed.
+ switch (CurrV.getKind()) {
+ case RefVal::Owned:
+ case RefVal::NotOwned:
+
+ if (PrevV.getCount() == CurrV.getCount())
+ return 0;
+
+ if (PrevV.getCount() > CurrV.getCount())
+ os << "Reference count decremented.";
+ else
+ os << "Reference count incremented.";
+
+ if (unsigned Count = CurrV.getCount()) {
+ os << " Object has +" << Count;
+
+ if (Count > 1)
+ os << " retain counts.";
+ else
+ os << " retain count.";
+ }
+
+ break;
+
+ case RefVal::Released:
+ os << "Object released.";
+ break;
+
+ case RefVal::ReturnedOwned:
+ os << "Object returned to caller as an owning reference (single retain "
+ "count transferred to caller).";
+ break;
+
+ case RefVal::ReturnedNotOwned:
+ os << "Object returned to caller with a +0 (non-owning) retain count.";
+ break;
+
+ default:
+ return NULL;
+ }
+
+ if (os.str().empty())
+ return 0; // We have nothing to say!
Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
FullSourceLoc Pos(S->getLocStart(), BR.getContext().getSourceManager());
@@ -2512,9 +2583,10 @@ CFRefLeakReport::getEndPath(BugReporter& br, const ExplodedNode<GRState>* EndN){
}
-CFRefLeakReport::CFRefLeakReport(CFRefBug& D, ExplodedNode<GRState> *n,
+CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf,
+ ExplodedNode<GRState> *n,
SymbolRef sym, GRExprEngine& Eng)
- : CFRefReport(D, n, sym)
+ : CFRefReport(D, tf, n, sym)
{
// Most bug reports are cached at the location where they occured.
@@ -2584,7 +2656,7 @@ void CFRefCount::EvalEndPath(GRExprEngine& Eng,
CFRefBug *BT = static_cast<CFRefBug*>(I->second ? leakAtReturn
: leakWithinFunction);
assert(BT && "BugType not initialized.");
- CFRefLeakReport* report = new CFRefLeakReport(*BT, N, I->first, Eng);
+ CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, I->first, Eng);
BR->EmitReport(report);
}
}
@@ -2633,7 +2705,7 @@ void CFRefCount::EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst,
CFRefBug *BT = static_cast<CFRefBug*>(I->second ? leakAtReturn
: leakWithinFunction);
assert(BT && "BugType not initialized.");
- CFRefLeakReport* report = new CFRefLeakReport(*BT, N, I->first, Eng);
+ CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, I->first, Eng);
BR->EmitReport(report);
}
}
@@ -2658,7 +2730,7 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet<GRState>& Dst,
BT = static_cast<CFRefBug*>(releaseNotOwned);
}
- CFRefReport *report = new CFRefReport(*BT, N, Sym);
+ CFRefReport *report = new CFRefReport(*BT, *this, N, Sym);
report->addRange(ErrorExpr->getSourceRange());
BR->EmitReport(report);
}