зеркало из https://github.com/microsoft/clang-1.git
Fix: <rdar://problem/5905851> do not report a leak when post-dominated by a call
to a noreturn or panic function git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@81803 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
31fddcca28
Коммит
06c9cb4d1a
|
@ -189,13 +189,21 @@ private:
|
||||||
const std::string Category;
|
const std::string Category;
|
||||||
llvm::FoldingSet<BugReportEquivClass> EQClasses;
|
llvm::FoldingSet<BugReportEquivClass> EQClasses;
|
||||||
friend class BugReporter;
|
friend class BugReporter;
|
||||||
|
bool SuppressonSink;
|
||||||
public:
|
public:
|
||||||
BugType(const char *name, const char* cat) : Name(name), Category(cat) {}
|
BugType(const char *name, const char* cat)
|
||||||
|
: Name(name), Category(cat), SuppressonSink(false) {}
|
||||||
virtual ~BugType();
|
virtual ~BugType();
|
||||||
|
|
||||||
// FIXME: Should these be made strings as well?
|
// FIXME: Should these be made strings as well?
|
||||||
const std::string& getName() const { return Name; }
|
const std::string& getName() const { return Name; }
|
||||||
const std::string& getCategory() const { return Category; }
|
const std::string& getCategory() const { return Category; }
|
||||||
|
|
||||||
|
/// isSuppressOnSink - Returns true if bug reports associated with this bug
|
||||||
|
/// 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; }
|
||||||
|
|
||||||
virtual void FlushReports(BugReporter& BR);
|
virtual void FlushReports(BugReporter& BR);
|
||||||
|
|
||||||
|
|
|
@ -1639,34 +1639,131 @@ void BugReporter::EmitReport(BugReport* R) {
|
||||||
EQ->AddReport(R);
|
EQ->AddReport(R);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Emitting reports in equivalence classes.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct VISIBILITY_HIDDEN FRIEC_WLItem {
|
||||||
|
const ExplodedNode *N;
|
||||||
|
ExplodedNode::const_succ_iterator I, E;
|
||||||
|
|
||||||
|
FRIEC_WLItem(const ExplodedNode *n)
|
||||||
|
: N(n), I(N->succ_begin()), E(N->succ_end()) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static BugReport *FindReportInEquivalenceClass(BugReportEquivClass& EQ) {
|
||||||
|
BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end();
|
||||||
|
assert(I != E);
|
||||||
|
BugReport *R = *I;
|
||||||
|
BugType& BT = R->getBugType();
|
||||||
|
|
||||||
|
if (!BT.isSuppressOnSink())
|
||||||
|
return R;
|
||||||
|
|
||||||
|
// For bug reports that should be suppressed when all paths are post-dominated
|
||||||
|
// by a sink node, iterate through the reports in the equivalence class
|
||||||
|
// until we find one that isn't post-dominated (if one exists). We use a
|
||||||
|
// DFS traversal of the ExplodedGraph to find a non-sink node. We could write
|
||||||
|
// this as a recursive function, but we don't want to risk blowing out the
|
||||||
|
// stack for very long paths.
|
||||||
|
for (; I != E; ++I) {
|
||||||
|
R = *I;
|
||||||
|
const ExplodedNode *N = R->getEndNode();
|
||||||
|
|
||||||
|
if (!N)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (N->isSink()) {
|
||||||
|
assert(false &&
|
||||||
|
"BugType::isSuppressSink() should not be 'true' for sink end nodes");
|
||||||
|
return R;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (N->succ_empty())
|
||||||
|
return R;
|
||||||
|
|
||||||
|
// At this point we know that 'N' is not a sink and it has at least one
|
||||||
|
// successor. Use a DFS worklist to find a non-sink end-of-path node.
|
||||||
|
typedef FRIEC_WLItem WLItem;
|
||||||
|
typedef llvm::SmallVector<WLItem, 10> DFSWorkList;
|
||||||
|
llvm::DenseMap<const ExplodedNode *, unsigned> Visited;
|
||||||
|
|
||||||
|
DFSWorkList WL;
|
||||||
|
WL.push_back(N);
|
||||||
|
Visited[N] = 1;
|
||||||
|
|
||||||
|
while (!WL.empty()) {
|
||||||
|
WLItem &WI = WL.back();
|
||||||
|
assert(!WI.N->succ_empty());
|
||||||
|
|
||||||
|
for (; WI.I != WI.E; ++WI.I) {
|
||||||
|
const ExplodedNode *Succ = *WI.I;
|
||||||
|
// End-of-path node?
|
||||||
|
if (Succ->succ_empty()) {
|
||||||
|
// If we found an end-of-path node that is not a sink, then return
|
||||||
|
// this report.
|
||||||
|
if (!Succ->isSink())
|
||||||
|
return R;
|
||||||
|
|
||||||
|
// Found a sink? Continue on to the next successor.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the successor as visited. If it hasn't been explored,
|
||||||
|
// enqueue it to the DFS worklist.
|
||||||
|
unsigned &mark = Visited[Succ];
|
||||||
|
if (!mark) {
|
||||||
|
mark = 1;
|
||||||
|
WL.push_back(Succ);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (&WL.back() == &WI)
|
||||||
|
WL.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach here, the end nodes for all reports in the equiavalence
|
||||||
|
// class are post-dominated by a sink node.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void BugReporter::FlushReport(BugReportEquivClass& EQ) {
|
void BugReporter::FlushReport(BugReportEquivClass& EQ) {
|
||||||
assert(!EQ.Reports.empty());
|
BugReport *R = FindReportInEquivalenceClass(EQ);
|
||||||
BugReport &R = **EQ.begin();
|
|
||||||
|
if (!R)
|
||||||
|
return;
|
||||||
|
|
||||||
PathDiagnosticClient* PD = getPathDiagnosticClient();
|
PathDiagnosticClient* PD = getPathDiagnosticClient();
|
||||||
|
|
||||||
// FIXME: Make sure we use the 'R' for the path that was actually used.
|
// FIXME: Make sure we use the 'R' for the path that was actually used.
|
||||||
// Probably doesn't make a difference in practice.
|
// Probably doesn't make a difference in practice.
|
||||||
BugType& BT = R.getBugType();
|
BugType& BT = R->getBugType();
|
||||||
|
|
||||||
llvm::OwningPtr<PathDiagnostic>
|
llvm::OwningPtr<PathDiagnostic>
|
||||||
D(new PathDiagnostic(R.getBugType().getName(),
|
D(new PathDiagnostic(R->getBugType().getName(),
|
||||||
!PD || PD->useVerboseDescription()
|
!PD || PD->useVerboseDescription()
|
||||||
? R.getDescription() : R.getShortDescription(),
|
? R->getDescription() : R->getShortDescription(),
|
||||||
BT.getCategory()));
|
BT.getCategory()));
|
||||||
|
|
||||||
GeneratePathDiagnostic(*D.get(), EQ);
|
GeneratePathDiagnostic(*D.get(), EQ);
|
||||||
|
|
||||||
// Get the meta data.
|
// Get the meta data.
|
||||||
std::pair<const char**, const char**> Meta = R.getExtraDescriptiveText();
|
std::pair<const char**, const char**> Meta = R->getExtraDescriptiveText();
|
||||||
for (const char** s = Meta.first; s != Meta.second; ++s) D->addMeta(*s);
|
for (const char** s = Meta.first; s != Meta.second; ++s)
|
||||||
|
D->addMeta(*s);
|
||||||
|
|
||||||
// Emit a summary diagnostic to the regular Diagnostics engine.
|
// Emit a summary diagnostic to the regular Diagnostics engine.
|
||||||
const SourceRange *Beg = 0, *End = 0;
|
const SourceRange *Beg = 0, *End = 0;
|
||||||
R.getRanges(Beg, End);
|
R->getRanges(Beg, End);
|
||||||
Diagnostic& Diag = getDiagnostic();
|
Diagnostic& Diag = getDiagnostic();
|
||||||
FullSourceLoc L(R.getLocation(), getSourceManager());
|
FullSourceLoc L(R->getLocation(), getSourceManager());
|
||||||
unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning,
|
unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning,
|
||||||
R.getShortDescription().c_str());
|
R->getShortDescription().c_str());
|
||||||
|
|
||||||
switch (End-Beg) {
|
switch (End-Beg) {
|
||||||
default: assert(0 && "Don't handle this many ranges yet!");
|
default: assert(0 && "Don't handle this many ranges yet!");
|
||||||
|
@ -1682,7 +1779,7 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
|
||||||
|
|
||||||
if (D->empty()) {
|
if (D->empty()) {
|
||||||
PathDiagnosticPiece* piece =
|
PathDiagnosticPiece* piece =
|
||||||
new PathDiagnosticEventPiece(L, R.getDescription());
|
new PathDiagnosticEventPiece(L, R->getDescription());
|
||||||
|
|
||||||
for ( ; Beg != End; ++Beg) piece->addRange(*Beg);
|
for ( ; Beg != End; ++Beg) piece->addRange(*Beg);
|
||||||
D->push_back(piece);
|
D->push_back(piece);
|
||||||
|
|
|
@ -2216,7 +2216,9 @@ void CFRefCount::RegisterChecks(BugReporter& BR) {
|
||||||
name = "Leak of returned object";
|
name = "Leak of returned object";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Leaks should not be reported if they are post-dominated by a sink.
|
||||||
leakAtReturn = new LeakAtReturn(this, name);
|
leakAtReturn = new LeakAtReturn(this, name);
|
||||||
|
leakAtReturn->setSuppressOnSink(true);
|
||||||
BR.Register(leakAtReturn);
|
BR.Register(leakAtReturn);
|
||||||
|
|
||||||
// Second, register leaks within a function/method.
|
// Second, register leaks within a function/method.
|
||||||
|
@ -2230,7 +2232,9 @@ void CFRefCount::RegisterChecks(BugReporter& BR) {
|
||||||
name = "Leak";
|
name = "Leak";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Leaks should not be reported if they are post-dominated by sinks.
|
||||||
leakWithinFunction = new LeakWithinFunction(this, name);
|
leakWithinFunction = new LeakWithinFunction(this, name);
|
||||||
|
leakWithinFunction->setSuppressOnSink(true);
|
||||||
BR.Register(leakWithinFunction);
|
BR.Register(leakWithinFunction);
|
||||||
|
|
||||||
// Save the reference to the BugReporter.
|
// Save the reference to the BugReporter.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче