analyzer:

- Improve -autorelease diagnostics.
- Improve VLA diagnostics.
- Use "short description" for bug when outputting to TextDiagnostics

llvm-svn: 71383
This commit is contained in:
Ted Kremenek 2009-05-10 05:11:21 +00:00
parent 1e0d6a5957
commit 3978f7972d
5 changed files with 56 additions and 41 deletions

View File

@ -1576,7 +1576,7 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
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.getDescription().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!");

View File

@ -1496,18 +1496,10 @@ public:
return RefVal(NotOwned, o, Count, 0, t); return RefVal(NotOwned, o, Count, 0, t);
} }
static RefVal makeReturnedOwned(unsigned Count) {
return RefVal(ReturnedOwned, Count);
}
static RefVal makeReturnedNotOwned() {
return RefVal(ReturnedNotOwned);
}
// Comparison, profiling, and pretty-printing. // Comparison, profiling, and pretty-printing.
bool operator==(const RefVal& X) const { bool operator==(const RefVal& X) const {
return kind == X.kind && Cnt == X.Cnt && T == X.T; return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt;
} }
RefVal operator-(size_t i) const { RefVal operator-(size_t i) const {
@ -1929,8 +1921,7 @@ namespace {
CFRefBug(tf, "Object sent -autorelease too many times") {} CFRefBug(tf, "Object sent -autorelease too many times") {}
const char *getDescription() const { const char *getDescription() const {
return "Object will be sent more -release messages from its containing " return "Object sent -autorelease too many times";
"autorelease pools than it has retain counts";
} }
}; };
@ -1971,6 +1962,10 @@ namespace {
ExplodedNode<GRState> *n, SymbolRef sym) ExplodedNode<GRState> *n, SymbolRef sym)
: RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {} : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {}
CFRefReport(CFRefBug& D, const CFRefCount &tf,
ExplodedNode<GRState> *n, SymbolRef sym, const char* endText)
: RangedBugReport(D, D.getDescription(), endText, n), Sym(sym), TF(tf) {}
virtual ~CFRefReport() {} virtual ~CFRefReport() {}
CFRefBug& getBugType() { CFRefBug& getBugType() {
@ -2274,7 +2269,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode<GRState>* N,
return 0; return 0;
assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
os << "Object added to autorelease pool."; os << "Object sent -autorelease message";
break; break;
} }
@ -2500,7 +2495,6 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,
return new PathDiagnosticEventPiece(L, os.str()); return new PathDiagnosticEventPiece(L, os.str());
} }
CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf,
ExplodedNode<GRState> *n, ExplodedNode<GRState> *n,
SymbolRef sym, GRExprEngine& Eng) SymbolRef sym, GRExprEngine& Eng)
@ -2983,20 +2977,6 @@ void CFRefCount::EvalReturn(ExplodedNodeSet<GRState>& Dst,
if (!T) if (!T)
return; return;
// Update the autorelease counts.
static unsigned autoreleasetag = 0;
GenericNodeBuilder Bd(Builder, S, &autoreleasetag);
bool stop = false;
llvm::tie(Pred, state) = HandleAutoreleaseCounts(state , Bd, Pred, Eng, Sym,
*T, stop);
if (stop)
return;
// Get the updated binding.
T = state.get<RefBindings>(Sym);
assert(T);
// Change the reference count. // Change the reference count.
RefVal X = *T; RefVal X = *T;
@ -3004,14 +2984,20 @@ void CFRefCount::EvalReturn(ExplodedNodeSet<GRState>& Dst,
case RefVal::Owned: { case RefVal::Owned: {
unsigned cnt = X.getCount(); unsigned cnt = X.getCount();
assert (cnt > 0); assert (cnt > 0);
X = RefVal::makeReturnedOwned(cnt - 1); X.setCount(cnt - 1);
X = X ^ RefVal::ReturnedOwned;
break; break;
} }
case RefVal::NotOwned: { case RefVal::NotOwned: {
unsigned cnt = X.getCount(); unsigned cnt = X.getCount();
X = cnt ? RefVal::makeReturnedOwned(cnt - 1) if (cnt) {
: RefVal::makeReturnedNotOwned(); X.setCount(cnt - 1);
X = X ^ RefVal::ReturnedOwned;
}
else {
X = X ^ RefVal::ReturnedNotOwned;
}
break; break;
} }
@ -3027,6 +3013,22 @@ void CFRefCount::EvalReturn(ExplodedNodeSet<GRState>& Dst,
if (!Pred) if (!Pred)
return; return;
// Update the autorelease counts.
static unsigned autoreleasetag = 0;
GenericNodeBuilder Bd(Builder, S, &autoreleasetag);
bool stop = false;
llvm::tie(Pred, state) = HandleAutoreleaseCounts(state , Bd, Pred, Eng, Sym,
X, stop);
// Did we cache out?
if (!Pred || stop)
return;
// Get the updated binding.
T = state.get<RefBindings>(Sym);
assert(T);
X = *T;
// Any leaks or other errors? // Any leaks or other errors?
if (X.isReturnedOwned() && X.getCount() == 0) { if (X.isReturnedOwned() && X.getCount() == 0) {
const Decl *CD = &Eng.getStateManager().getCodeDecl(); const Decl *CD = &Eng.getStateManager().getCodeDecl();
@ -3261,9 +3263,22 @@ CFRefCount::HandleAutoreleaseCounts(GRStateRef state, GenericNodeBuilder Bd,
if (ExplodedNode<GRState> *N = Bd.MakeNode(state, Pred)) { if (ExplodedNode<GRState> *N = Bd.MakeNode(state, Pred)) {
N->markAsSink(); N->markAsSink();
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
os << "Object over-autoreleased: object was sent -autorelease " ;
if (V.getAutoreleaseCount() > 1)
os << V.getAutoreleaseCount() << " times";
os << " but the object has ";
if (V.getCount() == 0)
os << "zero (locally visible)";
else
os << "+" << V.getCount();
os << " retain counts";
CFRefReport *report = CFRefReport *report =
new CFRefReport(*static_cast<CFRefBug*>(overAutorelease), new CFRefReport(*static_cast<CFRefBug*>(overAutorelease),
*this, N, Sym); *this, N, Sym, os.str().c_str());
BR->EmitReport(report); BR->EmitReport(report);
} }

View File

@ -453,8 +453,8 @@ public:
std::string shortBuf; std::string shortBuf;
llvm::raw_string_ostream os_short(shortBuf); llvm::raw_string_ostream os_short(shortBuf);
os_short << "Variable-length array '" << VD->getNameAsString() << "' " os_short << "Variable-length array '" << VD->getNameAsString() << "' "
<< (isUndefined ? " garbage value for array size" << (isUndefined ? "garbage value for array size"
: " has zero elements (undefined behavior)"); : "has zero elements (undefined behavior)");
RangedBugReport *report = new RangedBugReport(*this, RangedBugReport *report = new RangedBugReport(*this,
os_short.str().c_str(), os_short.str().c_str(),

View File

@ -103,12 +103,12 @@ void check_zero_sized_VLA(int x) {
if (x) if (x)
return; return;
int vla[x]; // expected-warning{{VLAs with no elements have undefined behavior}} int vla[x]; // expected-warning{{Variable-length array 'vla' has zero elements (undefined behavior)}}
} }
void check_uninit_sized_VLA() { void check_uninit_sized_VLA() {
int x; int x;
int vla[x]; // expected-warning{{The expression used to specify the number of elements in the variable-length array (VLA) 'vla' evaluates to an undefined or garbage value}} int vla[x]; // expected-warning{{Variable-length array 'vla' garbage value for array size}}
} }
// sizeof(void) // sizeof(void)

View File

@ -291,21 +291,21 @@ void f13_autorelease() {
void f13_autorelease_b() { void f13_autorelease_b() {
CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks);
[(id) A autorelease]; [(id) A autorelease];
[(id) A autorelease]; // expected-warning{{Object will be sent more -release messages from its containing autorelease pools than it has retain counts}} [(id) A autorelease]; // expected-warning{{Object sent -autorelease too many times}}
} }
CFMutableArrayRef f13_autorelease_c() { CFMutableArrayRef f13_autorelease_c() {
CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks);
[(id) A autorelease]; [(id) A autorelease];
[(id) A autorelease]; [(id) A autorelease];
return A; // expected-warning{{Object will be sent more -release messages from its containing autorelease pools than it has retain counts}} return A; // expected-warning{{Object sent -autorelease too many times}}
} }
CFMutableArrayRef f13_autorelease_d() { CFMutableArrayRef f13_autorelease_d() {
CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); CFMutableArrayRef A = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks);
[(id) A autorelease]; [(id) A autorelease];
[(id) A autorelease]; [(id) A autorelease];
CFMutableArrayRef B = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning{{Object will be sent more -release messages}} CFMutableArrayRef B = CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning{{Object sent -autorelease too many times}}
CFRelease(B); // no-warning CFRelease(B); // no-warning
} }