Skip to content

Commit 03466c2

Browse files
committed
Issue warnings about owned objects returned from a method that does not match the established Cocoa naming conventions.
llvm-svn: 58108
1 parent f3be44f commit 03466c2

File tree

1 file changed

+74
-54
lines changed

1 file changed

+74
-54
lines changed

clang/lib/Analysis/CFRefCount.cpp

Lines changed: 74 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ class VISIBILITY_HIDDEN RefVal {
10961096

10971097
static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
10981098

1099-
static bool isLeak(Kind k) { return k == ErrorLeak; }
1099+
static bool isLeak(Kind k) { return k >= ErrorLeak; }
11001100

11011101
bool isOwned() const {
11021102
return getKind() == Owned;
@@ -1266,7 +1266,8 @@ class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
12661266

12671267
typedef ReleasesNotOwnedTy UseAfterReleasesTy;
12681268

1269-
typedef llvm::DenseMap<GRExprEngine::NodeTy*, std::vector<SymbolID>*>
1269+
typedef llvm::DenseMap<GRExprEngine::NodeTy*,
1270+
std::vector<std::pair<SymbolID,bool> >*>
12701271
LeaksTy;
12711272

12721273
class BindingsPrinter : public GRState::Printer {
@@ -1302,9 +1303,9 @@ class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
13021303
const GRState* St,
13031304
RefVal::Kind hasErr, SymbolID Sym);
13041305

1305-
const GRState* HandleSymbolDeath(GRStateManager& VMgr, const GRState* St,
1306-
const Decl* CD, SymbolID sid, RefVal V,
1307-
bool& hasLeak);
1306+
std::pair<GRStateRef, bool>
1307+
HandleSymbolDeath(GRStateManager& VMgr, const GRState* St,
1308+
const Decl* CD, SymbolID sid, RefVal V, bool& hasLeak);
13081309

13091310
public:
13101311

@@ -1508,6 +1509,7 @@ void CFRefCount::EvalSummary(ExplodedNodeSet<GRState>& Dst,
15081509

15091510
// Get the state.
15101511
GRStateRef state(Builder.GetState(Pred), Eng.getStateManager());
1512+
ASTContext& Ctx = Eng.getStateManager().getContext();
15111513

15121514
// Evaluate the effect of the arguments.
15131515
RefVal::Kind hasErr = (RefVal::Kind) 0;
@@ -1560,7 +1562,7 @@ void CFRefCount::EvalSummary(ExplodedNodeSet<GRState>& Dst,
15601562
if (R) {
15611563
// Set the value of the variable to be a conjured symbol.
15621564
unsigned Count = Builder.getCurrentBlockCount();
1563-
QualType T = R->getType();
1565+
QualType T = R->getType(Ctx);
15641566

15651567
// FIXME: handle structs.
15661568
if (T->isIntegerType() || Loc::IsLocType(T)) {
@@ -1743,7 +1745,7 @@ void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst,
17431745
}
17441746

17451747
Summ = Summaries.getMethodSummary(ME, ID);
1746-
#if 0
1748+
17471749
// Special-case: are we sending a mesage to "self"?
17481750
// This is a hack. When we have full-IP this should be removed.
17491751
if (!Summ) {
@@ -1754,17 +1756,15 @@ void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<GRState>& Dst,
17541756
if (Expr* Receiver = ME->getReceiver()) {
17551757
SVal X = Eng.getStateManager().GetSVal(St, Receiver);
17561758
if (loc::MemRegionVal* L = dyn_cast<loc::MemRegionVal>(&X))
1757-
if (const VarRegion* R = dyn_cast<VarRegion>(L->getRegion()))
1758-
if (R->getDecl() == MD->getSelfDecl()) {
1759-
// Create a summmary where all of the arguments "StopTracking".
1760-
Summ = Summaries.getPersistentSummary(RetEffect::MakeNoRet(),
1761-
DoNothing,
1762-
StopTracking);
1763-
}
1759+
if (L->getRegion() == Eng.getStateManager().getSelfRegion(St)) {
1760+
// Create a summmary where all of the arguments "StopTracking".
1761+
Summ = Summaries.getPersistentSummary(RetEffect::MakeNoRet(),
1762+
DoNothing,
1763+
StopTracking);
1764+
}
17641765
}
17651766
}
17661767
}
1767-
#endif
17681768
}
17691769
else
17701770
Summ = Summaries.getClassMethodSummary(ME->getClassName(),
@@ -1846,42 +1846,41 @@ void CFRefCount::EvalStore(ExplodedNodeSet<GRState>& Dst,
18461846
// or autorelease. Any other time you receive an object, you must
18471847
// not release it."
18481848
//
1849-
#if 0
18501849
static bool followsFundamentalRule(const char* s) {
18511850
return CStrInCStrNoCase(s, "create") || CStrInCStrNoCase(s, "copy") ||
18521851
CStrInCStrNoCase(s, "new");
18531852
}
1854-
#endif
18551853

1856-
const GRState* CFRefCount::HandleSymbolDeath(GRStateManager& VMgr,
1857-
const GRState* St, const Decl* CD,
1858-
SymbolID sid,
1859-
RefVal V, bool& hasLeak) {
1854+
std::pair<GRStateRef,bool>
1855+
CFRefCount::HandleSymbolDeath(GRStateManager& VMgr,
1856+
const GRState* St, const Decl* CD,
1857+
SymbolID sid,
1858+
RefVal V, bool& hasLeak) {
18601859

18611860
GRStateRef state(St, VMgr);
18621861
assert (!V.isReturnedOwned() || CD &&
18631862
"CodeDecl must be available for reporting ReturnOwned errors.");
18641863

1865-
#if 0
18661864
if (V.isReturnedOwned() && V.getCount() == 0)
18671865
if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(CD)) {
18681866
std::string s = MD->getSelector().getName();
18691867
if (!followsFundamentalRule(s.c_str())) {
18701868
hasLeak = true;
1871-
return state.set<RefBindings>(sid, V ^ RefVal::ErrorLeakReturned);
1869+
state = state.set<RefBindings>(sid, V ^ RefVal::ErrorLeakReturned);
1870+
return std::make_pair(state, true);
18721871
}
18731872
}
1874-
#endif
18751873

18761874
// All other cases.
18771875

18781876
hasLeak = V.isOwned() ||
18791877
((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0);
18801878

18811879
if (!hasLeak)
1882-
return state.remove<RefBindings>(sid);
1880+
return std::make_pair(state.remove<RefBindings>(sid), false);
18831881

1884-
return state.set<RefBindings>(sid, V ^ RefVal::ErrorLeak);
1882+
return std::make_pair(state.set<RefBindings>(sid, V ^ RefVal::ErrorLeak),
1883+
false);
18851884
}
18861885

18871886
void CFRefCount::EvalEndPath(GRExprEngine& Eng,
@@ -1890,16 +1889,18 @@ void CFRefCount::EvalEndPath(GRExprEngine& Eng,
18901889
const GRState* St = Builder.getState();
18911890
RefBindings B = St->get<RefBindings>();
18921891

1893-
llvm::SmallVector<SymbolID, 10> Leaked;
1892+
llvm::SmallVector<std::pair<SymbolID, bool>, 10> Leaked;
18941893
const Decl* CodeDecl = &Eng.getGraph().getCodeDecl();
18951894

18961895
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) {
18971896
bool hasLeak = false;
18981897

1899-
St = HandleSymbolDeath(Eng.getStateManager(), St, CodeDecl,
1900-
(*I).first, (*I).second, hasLeak);
1898+
std::pair<GRStateRef, bool> X =
1899+
HandleSymbolDeath(Eng.getStateManager(), St, CodeDecl,
1900+
(*I).first, (*I).second, hasLeak);
19011901

1902-
if (hasLeak) Leaked.push_back((*I).first);
1902+
St = X.first;
1903+
if (hasLeak) Leaked.push_back(std::make_pair((*I).first, X.second));
19031904
}
19041905

19051906
if (Leaked.empty())
@@ -1910,12 +1911,12 @@ void CFRefCount::EvalEndPath(GRExprEngine& Eng,
19101911
if (!N)
19111912
return;
19121913

1913-
std::vector<SymbolID>*& LeaksAtNode = Leaks[N];
1914+
std::vector<std::pair<SymbolID,bool> >*& LeaksAtNode = Leaks[N];
19141915
assert (!LeaksAtNode);
1915-
LeaksAtNode = new std::vector<SymbolID>();
1916+
LeaksAtNode = new std::vector<std::pair<SymbolID,bool> >();
19161917

1917-
for (llvm::SmallVector<SymbolID, 10>::iterator I=Leaked.begin(),
1918-
E = Leaked.end(); I != E; ++I)
1918+
for (llvm::SmallVector<std::pair<SymbolID,bool>, 10>::iterator
1919+
I = Leaked.begin(), E = Leaked.end(); I != E; ++I)
19191920
(*LeaksAtNode).push_back(*I);
19201921
}
19211922

@@ -1932,7 +1933,7 @@ void CFRefCount::EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst,
19321933
// FIXME: a lot of copy-and-paste from EvalEndPath. Refactor.
19331934

19341935
RefBindings B = St->get<RefBindings>();
1935-
llvm::SmallVector<SymbolID, 10> Leaked;
1936+
llvm::SmallVector<std::pair<SymbolID,bool>, 10> Leaked;
19361937

19371938
for (GRStateManager::DeadSymbolsTy::const_iterator
19381939
I=Dead.begin(), E=Dead.end(); I!=E; ++I) {
@@ -1944,10 +1945,13 @@ void CFRefCount::EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst,
19441945

19451946
bool hasLeak = false;
19461947

1947-
St = HandleSymbolDeath(Eng.getStateManager(), St, 0, *I, *T, hasLeak);
1948+
std::pair<GRStateRef, bool> X
1949+
= HandleSymbolDeath(Eng.getStateManager(), St, 0, *I, *T, hasLeak);
1950+
1951+
St = X.first;
19481952

19491953
if (hasLeak)
1950-
Leaked.push_back(*I);
1954+
Leaked.push_back(std::make_pair(*I,X.second));
19511955
}
19521956

19531957
if (Leaked.empty())
@@ -1958,12 +1962,12 @@ void CFRefCount::EvalDeadSymbols(ExplodedNodeSet<GRState>& Dst,
19581962
if (!N)
19591963
return;
19601964

1961-
std::vector<SymbolID>*& LeaksAtNode = Leaks[N];
1965+
std::vector<std::pair<SymbolID,bool> >*& LeaksAtNode = Leaks[N];
19621966
assert (!LeaksAtNode);
1963-
LeaksAtNode = new std::vector<SymbolID>();
1967+
LeaksAtNode = new std::vector<std::pair<SymbolID,bool> >();
19641968

1965-
for (llvm::SmallVector<SymbolID, 10>::iterator I=Leaked.begin(),
1966-
E = Leaked.end(); I != E; ++I)
1969+
for (llvm::SmallVector<std::pair<SymbolID,bool>, 10>::iterator
1970+
I = Leaked.begin(), E = Leaked.end(); I != E; ++I)
19671971
(*LeaksAtNode).push_back(*I);
19681972
}
19691973

@@ -2196,19 +2200,34 @@ namespace {
21962200
};
21972201

21982202
class VISIBILITY_HIDDEN Leak : public CFRefBug {
2203+
bool isReturn;
21992204
public:
22002205
Leak(CFRefCount& tf) : CFRefBug(tf) {}
22012206

2207+
void setIsReturn(bool x) { isReturn = x; }
2208+
22022209
virtual const char* getName() const {
22032210

2204-
if (getTF().isGCEnabled())
2205-
return "leak (GC)";
2206-
2207-
if (getTF().getLangOptions().getGCMode() == LangOptions::HybridGC)
2208-
return "leak (hybrid MM, non-GC)";
2209-
2210-
assert (getTF().getLangOptions().getGCMode() == LangOptions::NonGC);
2211-
return "leak";
2211+
if (!isReturn) {
2212+
if (getTF().isGCEnabled())
2213+
return "leak (GC)";
2214+
2215+
if (getTF().getLangOptions().getGCMode() == LangOptions::HybridGC)
2216+
return "leak (hybrid MM, non-GC)";
2217+
2218+
assert (getTF().getLangOptions().getGCMode() == LangOptions::NonGC);
2219+
return "leak";
2220+
}
2221+
else {
2222+
if (getTF().isGCEnabled())
2223+
return "leak of returned object (GC)";
2224+
2225+
if (getTF().getLangOptions().getGCMode() == LangOptions::HybridGC)
2226+
return "leak of returned object (hybrid MM, non-GC)";
2227+
2228+
assert (getTF().getLangOptions().getGCMode() == LangOptions::NonGC);
2229+
return "leak of returned object";
2230+
}
22122231
}
22132232

22142233
virtual const char* getDescription() const {
@@ -2410,8 +2429,8 @@ PathDiagnosticPiece* CFRefReport::VisitNode(ExplodedNode<GRState>* N,
24102429
break;
24112430

24122431
case RefVal::ReturnedOwned:
2413-
Msg = "Object returned to caller as owning reference (single retain count"
2414-
" transferred to caller).";
2432+
Msg = "Object returned to caller as an owning reference (single retain "
2433+
"count transferred to caller).";
24152434
break;
24162435

24172436
case RefVal::ReturnedNotOwned:
@@ -2630,11 +2649,12 @@ void Leak::EmitWarnings(BugReporter& BR) {
26302649
for (CFRefCount::leaks_iterator I = TF.leaks_begin(),
26312650
E = TF.leaks_end(); I != E; ++I) {
26322651

2633-
std::vector<SymbolID>& SymV = *(I->second);
2652+
std::vector<std::pair<SymbolID, bool> >& SymV = *(I->second);
26342653
unsigned n = SymV.size();
26352654

26362655
for (unsigned i = 0; i < n; ++i) {
2637-
CFRefReport report(*this, I->first, SymV[i]);
2656+
setIsReturn(SymV[i].second);
2657+
CFRefReport report(*this, I->first, SymV[i].first);
26382658
BR.EmitWarning(report);
26392659
}
26402660
}
@@ -2650,7 +2670,7 @@ bool Leak::isCached(BugReport& R) {
26502670

26512671
// Most bug reports are cached at the location where they occured.
26522672
// With leaks, we want to unique them by the location where they were
2653-
// allocated, and only report only a single path.
2673+
// allocated, and only report a single path.
26542674

26552675
SymbolID Sym = static_cast<CFRefReport&>(R).getSymbol();
26562676

0 commit comments

Comments
 (0)