BugReport::VisitNode now takes BugReporter& instead of ASTContext&.

Shuffled around code in CFRefCount to better pair classes with implementation,
and started adding subclasses of RangedBugReport to handle better diagnostics
for reference count bugs.

llvm-svn: 49889
This commit is contained in:
Ted Kremenek 2008-04-18 03:39:05 +00:00
parent a3d02636ef
commit 396f43620f
3 changed files with 158 additions and 102 deletions

View File

@ -74,7 +74,7 @@ public:
virtual PathDiagnosticPiece* VisitNode(ExplodedNode<ValueState>* N, virtual PathDiagnosticPiece* VisitNode(ExplodedNode<ValueState>* N,
ExplodedNode<ValueState>* PrevN, ExplodedNode<ValueState>* PrevN,
ExplodedGraph<ValueState>& G, ExplodedGraph<ValueState>& G,
ASTContext& Ctx); BugReporter& BR);
}; };
class RangedBugReport : public BugReport { class RangedBugReport : public BugReport {

View File

@ -135,7 +135,7 @@ FullSourceLoc BugReport::getLocation(SourceManager& Mgr) {
PathDiagnosticPiece* BugReport::VisitNode(ExplodedNode<ValueState>* N, PathDiagnosticPiece* BugReport::VisitNode(ExplodedNode<ValueState>* N,
ExplodedNode<ValueState>* PrevN, ExplodedNode<ValueState>* PrevN,
ExplodedGraph<ValueState>& G, ExplodedGraph<ValueState>& G,
ASTContext& Ctx) { BugReporter& BR) {
return NULL; return NULL;
} }
@ -352,7 +352,7 @@ void BugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
} }
} }
else else
if (PathDiagnosticPiece* piece = R.VisitNode(N, NextNode, *GTrim, Ctx)) if (PathDiagnosticPiece* piece = R.VisitNode(N, NextNode, *GTrim, *this))
PD.push_front(piece); PD.push_front(piece);
} }
} }

View File

@ -413,72 +413,6 @@ CFRefSummaryManager::getCFSummaryGetRule(FunctionTypeProto* FT) {
return getPersistentSummary(getArgEffects(), RetEffect::MakeNotOwned()); return getPersistentSummary(getArgEffects(), RetEffect::MakeNotOwned());
} }
//===----------------------------------------------------------------------===//
// Bug Descriptions.
//===----------------------------------------------------------------------===//
namespace {
class CFRefCount;
class VISIBILITY_HIDDEN CFRefBug : public BugType {
protected:
CFRefCount& TF;
public:
CFRefBug(CFRefCount& tf) : TF(tf) {}
};
class VISIBILITY_HIDDEN UseAfterRelease : public CFRefBug {
public:
UseAfterRelease(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
return "(CoreFoundation) use-after-release";
}
virtual const char* getDescription() const {
return "(CoreFoundation) Reference-counted object is used"
" after it is released.";
}
virtual void EmitWarnings(BugReporter& BR);
};
class VISIBILITY_HIDDEN BadRelease : public CFRefBug {
public:
BadRelease(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
return "(CoreFoundation) release of non-owned object";
}
virtual const char* getDescription() const {
return "Incorrect decrement of the reference count of a "
"CoreFoundation object:\n"
"The object is not owned at this point by the caller.";
}
virtual void EmitWarnings(BugReporter& BR);
};
class VISIBILITY_HIDDEN Leak : public CFRefBug {
public:
Leak(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
return "(CoreFoundation) Memory Leak";
}
virtual const char* getDescription() const {
return "The CoreFoundation object has an excessive reference count and"
"\nis leaked after this statement.";
}
virtual void EmitWarnings(BugReporter& BR);
};
} // end anonymous namespace
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Reference-counting logic (typestate + counts). // Reference-counting logic (typestate + counts).
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -634,25 +568,27 @@ void RefVal::print(std::ostream& Out) const {
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals { class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
public:
// Type definitions. // Type definitions.
typedef llvm::ImmutableMap<SymbolID, RefVal> RefBindings; typedef llvm::ImmutableMap<SymbolID, RefVal> RefBindings;
typedef RefBindings::Factory RefBFactoryTy; typedef RefBindings::Factory RefBFactoryTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*,Expr*> UseAfterReleasesTy; typedef llvm::DenseMap<GRExprEngine::NodeTy*,std::pair<Expr*, SymbolID> >
typedef llvm::DenseMap<GRExprEngine::NodeTy*,Expr*> ReleasesNotOwnedTy; ReleasesNotOwnedTy;
typedef llvm::SmallVector<std::pair<SymbolID, ExplodedNode<ValueState>*>, 2> typedef ReleasesNotOwnedTy UseAfterReleasesTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*, std::vector<SymbolID>*>
LeaksTy; LeaksTy;
class BindingsPrinter : public ValueState::CheckerStatePrinter { class BindingsPrinter : public ValueState::CheckerStatePrinter {
public: public:
virtual void PrintCheckerState(std::ostream& Out, void* State, virtual void PrintCheckerState(std::ostream& Out, void* State,
const char* nl, const char* sep); const char* nl, const char* sep);
}; };
private:
// Instance variables. // Instance variables.
CFRefSummaryManager Summaries; CFRefSummaryManager Summaries;
@ -667,12 +603,14 @@ class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
Selector RetainSelector; Selector RetainSelector;
Selector ReleaseSelector; Selector ReleaseSelector;
// Private methods. public:
static RefBindings GetRefBindings(ValueState& StImpl) { static RefBindings GetRefBindings(ValueState& StImpl) {
return RefBindings((RefBindings::TreeTy*) StImpl.CheckerState); return RefBindings((RefBindings::TreeTy*) StImpl.CheckerState);
} }
private:
static void SetRefBindings(ValueState& StImpl, RefBindings B) { static void SetRefBindings(ValueState& StImpl, RefBindings B) {
StImpl.CheckerState = B.getRoot(); StImpl.CheckerState = B.getRoot();
} }
@ -689,7 +627,7 @@ class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
Expr* NodeExpr, Expr* ErrorExpr, Expr* NodeExpr, Expr* ErrorExpr,
ExplodedNode<ValueState>* Pred, ExplodedNode<ValueState>* Pred,
ValueState* St, ValueState* St,
RefVal::Kind hasErr); RefVal::Kind hasErr, SymbolID Sym);
ValueState* HandleSymbolDeath(ValueStateManager& VMgr, ValueState* St, ValueState* HandleSymbolDeath(ValueStateManager& VMgr, ValueState* St,
SymbolID sid, RefVal V, bool& hasLeak); SymbolID sid, RefVal V, bool& hasLeak);
@ -704,7 +642,10 @@ public:
RetainSelector(GetUnarySelector("retain", Ctx)), RetainSelector(GetUnarySelector("retain", Ctx)),
ReleaseSelector(GetUnarySelector("release", Ctx)) {} ReleaseSelector(GetUnarySelector("release", Ctx)) {}
virtual ~CFRefCount() {} virtual ~CFRefCount() {
for (LeaksTy::iterator I = Leaks.begin(), E = Leaks.end(); I!=E; ++I)
delete I->second;
}
virtual void RegisterChecks(GRExprEngine& Eng); virtual void RegisterChecks(GRExprEngine& Eng);
@ -770,12 +711,7 @@ public:
} // end anonymous namespace } // end anonymous namespace
void CFRefCount::RegisterChecks(GRExprEngine& Eng) {
GRSimpleVals::RegisterChecks(Eng);
Eng.Register(new UseAfterRelease(*this));
Eng.Register(new BadRelease(*this));
Eng.Register(new Leak(*this));
}
void CFRefCount::BindingsPrinter::PrintCheckerState(std::ostream& Out, void CFRefCount::BindingsPrinter::PrintCheckerState(std::ostream& Out,
@ -806,7 +742,7 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet<ValueState>& Dst,
Expr* NodeExpr, Expr* ErrorExpr, Expr* NodeExpr, Expr* ErrorExpr,
ExplodedNode<ValueState>* Pred, ExplodedNode<ValueState>* Pred,
ValueState* St, ValueState* St,
RefVal::Kind hasErr) { RefVal::Kind hasErr, SymbolID Sym) {
Builder.BuildSinks = true; Builder.BuildSinks = true;
GRExprEngine::NodeTy* N = Builder.MakeNode(Dst, NodeExpr, Pred, St); GRExprEngine::NodeTy* N = Builder.MakeNode(Dst, NodeExpr, Pred, St);
@ -815,11 +751,11 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet<ValueState>& Dst,
switch (hasErr) { switch (hasErr) {
default: assert(false); default: assert(false);
case RefVal::ErrorUseAfterRelease: case RefVal::ErrorUseAfterRelease:
UseAfterReleases[N] = ErrorExpr; UseAfterReleases[N] = std::make_pair(ErrorExpr, Sym);
break; break;
case RefVal::ErrorReleaseNotOwned: case RefVal::ErrorReleaseNotOwned:
ReleasesNotOwned[N] = ErrorExpr; ReleasesNotOwned[N] = std::make_pair(ErrorExpr, Sym);
break; break;
} }
} }
@ -856,6 +792,7 @@ void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
unsigned idx = 0; unsigned idx = 0;
Expr* ErrorExpr = NULL; Expr* ErrorExpr = NULL;
SymbolID ErrorSym = 0;
for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();
I != E; ++I, ++idx) { I != E; ++I, ++idx) {
@ -872,6 +809,7 @@ void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
if (hasErr) { if (hasErr) {
ErrorExpr = *I; ErrorExpr = *I;
ErrorSym = T->getValue().first;
break; break;
} }
} }
@ -887,7 +825,8 @@ void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
St = StateMgr.getPersistentState(StVals); St = StateMgr.getPersistentState(StVals);
if (hasErr) { if (hasErr) {
ProcessNonLeakError(Dst, Builder, CE, ErrorExpr, Pred, St, hasErr); ProcessNonLeakError(Dst, Builder, CE, ErrorExpr, Pred, St,
hasErr, ErrorSym);
return; return;
} }
@ -1031,7 +970,7 @@ bool CFRefCount::EvalObjCMessageExprAux(ExplodedNodeSet<ValueState>& Dst,
// Create an error node if it exists. // Create an error node if it exists.
if (hasErr) if (hasErr)
ProcessNonLeakError(Dst, Builder, ME, Receiver, Pred, St, hasErr); ProcessNonLeakError(Dst, Builder, ME, Receiver, Pred, St, hasErr, Sym);
else else
Builder.MakeNode(Dst, ME, Pred, St); Builder.MakeNode(Dst, ME, Pred, St);
@ -1125,10 +1064,13 @@ void CFRefCount::EvalEndPath(GRExprEngine& Eng,
} }
ExplodedNode<ValueState>* N = Builder.MakeNode(St); ExplodedNode<ValueState>* N = Builder.MakeNode(St);
std::vector<SymbolID>*& LeaksAtNode = Leaks[N];
assert (!LeaksAtNode);
LeaksAtNode = new std::vector<SymbolID>();
for (llvm::SmallVector<SymbolID, 10>::iterator I=Leaked.begin(), for (llvm::SmallVector<SymbolID, 10>::iterator I=Leaked.begin(),
E = Leaked.end(); I != E; ++I) E = Leaked.end(); I != E; ++I)
Leaks.push_back(std::make_pair(*I, N)); (*LeaksAtNode).push_back(*I);
} }
// Return statements. // Return statements.
@ -1274,13 +1216,123 @@ CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
// Error reporting. // Error reporting.
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
namespace {
//===-------------===//
// Bug Descriptions. //
//===-------------===//
class VISIBILITY_HIDDEN CFRefBug : public BugType {
protected:
CFRefCount& TF;
public:
CFRefBug(CFRefCount& tf) : TF(tf) {}
};
class VISIBILITY_HIDDEN UseAfterRelease : public CFRefBug {
public:
UseAfterRelease(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
return "(CoreFoundation) use-after-release";
}
virtual const char* getDescription() const {
return "(CoreFoundation) Reference-counted object is used"
" after it is released.";
}
virtual void EmitWarnings(BugReporter& BR);
};
class VISIBILITY_HIDDEN BadRelease : public CFRefBug {
public:
BadRelease(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
return "(CoreFoundation) release of non-owned object";
}
virtual const char* getDescription() const {
return "Incorrect decrement of the reference count of a "
"CoreFoundation object:\n"
"The object is not owned at this point by the caller.";
}
virtual void EmitWarnings(BugReporter& BR);
};
class VISIBILITY_HIDDEN Leak : public CFRefBug {
public:
Leak(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
return "(CoreFoundation) Memory Leak";
}
virtual const char* getDescription() const {
return "The CoreFoundation object has an excessive reference count and"
"\nis leaked after this statement.";
}
virtual void EmitWarnings(BugReporter& BR);
};
//===---------===//
// Bug Reports. //
//===---------===//
class VISIBILITY_HIDDEN CFRefReport : public RangedBugReport {
SymbolID Sym;
public:
CFRefReport(const BugType& D, ExplodedNode<ValueState> *n, SymbolID sym)
: RangedBugReport(D, n), Sym(sym) {}
virtual ~CFRefReport() {}
virtual PathDiagnosticPiece* VisitNode(ExplodedNode<ValueState>* N,
ExplodedNode<ValueState>* PrevN,
ExplodedGraph<ValueState>& G,
BugReporter& BR);
};
} // end anonymous namespace
void CFRefCount::RegisterChecks(GRExprEngine& Eng) {
GRSimpleVals::RegisterChecks(Eng);
Eng.Register(new UseAfterRelease(*this));
Eng.Register(new BadRelease(*this));
Eng.Register(new Leak(*this));
}
PathDiagnosticPiece* CFRefReport::VisitNode(ExplodedNode<ValueState>* N,
ExplodedNode<ValueState>* PrevN,
ExplodedGraph<ValueState>& G,
BugReporter& BR) {
// Check if the type state has changed.
ValueState* PrevSt = PrevN->getState();
ValueState* CurrSt = N->getState();
CFRefCount::RefBindings PrevB = CFRefCount::GetRefBindings(*PrevSt);
CFRefCount::RefBindings CurrB = CFRefCount::GetRefBindings(*CurrSt);
return NULL;
}
void UseAfterRelease::EmitWarnings(BugReporter& BR) { void UseAfterRelease::EmitWarnings(BugReporter& BR) {
for (CFRefCount::use_after_iterator I = TF.use_after_begin(), for (CFRefCount::use_after_iterator I = TF.use_after_begin(),
E = TF.use_after_end(); I != E; ++I) { E = TF.use_after_end(); I != E; ++I) {
RangedBugReport report(*this, I->first); CFRefReport report(*this, I->first, I->second.second);
report.addRange(I->second->getSourceRange()); report.addRange(I->second.first->getSourceRange());
BR.EmitWarning(report); BR.EmitWarning(report);
} }
} }
@ -1290,10 +1342,9 @@ void BadRelease::EmitWarnings(BugReporter& BR) {
for (CFRefCount::bad_release_iterator I = TF.bad_release_begin(), for (CFRefCount::bad_release_iterator I = TF.bad_release_begin(),
E = TF.bad_release_end(); I != E; ++I) { E = TF.bad_release_end(); I != E; ++I) {
RangedBugReport report(*this, I->first); CFRefReport report(*this, I->first, I->second.second);
report.addRange(I->second->getSourceRange()); report.addRange(I->second.first->getSourceRange());
BR.EmitWarning(report); BR.EmitWarning(report);
} }
} }
@ -1302,10 +1353,15 @@ void Leak::EmitWarnings(BugReporter& BR) {
for (CFRefCount::leaks_iterator I = TF.leaks_begin(), for (CFRefCount::leaks_iterator I = TF.leaks_begin(),
E = TF.leaks_end(); I != E; ++I) { E = TF.leaks_end(); I != E; ++I) {
BugReport report(*this, I->second); std::vector<SymbolID>& SymV = *(I->second);
unsigned n = SymV.size();
for (unsigned i = 0; i < n; ++i) {
CFRefReport report(*this, I->first, SymV[i]);
BR.EmitWarning(report); BR.EmitWarning(report);
} }
} }
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Transfer function creation for external clients. // Transfer function creation for external clients.