return true;
}
-//===----------------------------------------------------------------------===//
-// Handle return statements.
-//===----------------------------------------------------------------------===//
-
-void RetainCountChecker::checkPreStmt(const ReturnStmt *S,
- CheckerContext &C) const {
+ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S,
+ CheckerContext &C) const {
+ ExplodedNode *Pred = C.getPredecessor();
// Only adjust the reference count if this is the top-level call frame,
// and not the result of inlining. In the future, we should do
// with their expected semantics (e.g., the method should return a retained
// object, etc.).
if (!C.inTopFrame())
- return;
+ return Pred;
+
+ if (!S)
+ return Pred;
const Expr *RetE = S->getRetValue();
if (!RetE)
- return;
+ return Pred;
ProgramStateRef state = C.getState();
SymbolRef Sym =
state->getSValAsScalarOrLoc(RetE, C.getLocationContext()).getAsLocSymbol();
if (!Sym)
- return;
+ return Pred;
// Get the reference count binding (if any).
const RefVal *T = getRefBinding(state, Sym);
if (!T)
- return;
+ return Pred;
// Change the reference count.
RefVal X = *T;
if (cnt) {
X.setCount(cnt - 1);
X = X ^ RefVal::ReturnedOwned;
- }
- else {
+ } else {
X = X ^ RefVal::ReturnedNotOwned;
}
break;
}
default:
- return;
+ return Pred;
}
// Update the binding.
state = setRefBinding(state, Sym, X);
- ExplodedNode *Pred = C.addTransition(state);
+ Pred = C.addTransition(state);
// At this point we have updated the state properly.
// Everything after this is merely checking to see if the return value has
// Did we cache out?
if (!Pred)
- return;
+ return nullptr;
// Update the autorelease counts.
static CheckerProgramPointTag AutoreleaseTag(this, "Autorelease");
- state = handleAutoreleaseCounts(state, Pred, &AutoreleaseTag, C, Sym, X);
+ state = handleAutoreleaseCounts(state, Pred, &AutoreleaseTag, C, Sym, X, S);
- // Did we cache out?
+ // Have we generated a sink node?
if (!state)
- return;
+ return nullptr;
// Get the updated binding.
T = getRefBinding(state, Sym);
}
}
- checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
+ return checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
}
-void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
+ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
CheckerContext &C,
ExplodedNode *Pred,
RetEffect RE, RefVal X,
// [self addSubview:_contentView]; // invalidates 'self'
// [_contentView release];
if (X.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
- return;
+ return Pred;
// Any leaks or other errors?
if (X.isReturnedOwned() && X.getCount() == 0) {
if (RE.getKind() != RetEffect::NoRet) {
- bool hasError = false;
if (!RE.isOwned()) {
+
// The returning type is a CF, we expect the enclosing method should
// return ownership.
- hasError = true;
X = X ^ RefVal::ErrorLeakReturned;
- }
- if (hasError) {
// Generate an error node.
state = setRefBinding(state, Sym, X);
ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag);
if (N) {
const LangOptions &LOpts = C.getASTContext().getLangOpts();
- C.emitReport(llvm::make_unique<CFRefLeakReport>(
+ auto R = llvm::make_unique<CFRefLeakReport>(
*getLeakAtReturnBug(LOpts), LOpts, SummaryLog, N, Sym, C,
- IncludeAllocationLine));
+ IncludeAllocationLine);
+ C.emitReport(std::move(R));
}
+ return N;
}
}
} else if (X.isReturnedNotOwned()) {
if (!returnNotOwnedForOwned)
returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this));
- C.emitReport(llvm::make_unique<CFRefReport>(
+ auto R = llvm::make_unique<CFRefReport>(
*returnNotOwnedForOwned, C.getASTContext().getLangOpts(),
- SummaryLog, N, Sym));
+ SummaryLog, N, Sym);
+ C.emitReport(std::move(R));
}
+ return N;
}
}
}
+ return Pred;
}
//===----------------------------------------------------------------------===//
return state;
}
-//===----------------------------------------------------------------------===//
-// Handle dead symbols and end-of-path.
-//===----------------------------------------------------------------------===//
-
ProgramStateRef
RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
ExplodedNode *Pred,
const ProgramPointTag *Tag,
CheckerContext &Ctx,
- SymbolRef Sym, RefVal V) const {
+ SymbolRef Sym,
+ RefVal V,
+ const ReturnStmt *S) const {
unsigned ACnt = V.getAutoreleaseCount();
// No autorelease counts? Nothing to be done.
if (ACnt <= Cnt) {
if (ACnt == Cnt) {
V.clearCounts();
- if (V.getKind() == RefVal::ReturnedOwned)
+ if (V.getKind() == RefVal::ReturnedOwned) {
V = V ^ RefVal::ReturnedNotOwned;
- else
+ } else {
V = V ^ RefVal::NotOwned;
+ }
} else {
V.setCount(V.getCount() - ACnt);
V.setAutoreleaseCount(0);
overAutorelease.reset(new OverAutorelease(this));
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
- Ctx.emitReport(llvm::make_unique<CFRefReport>(
- *overAutorelease, LOpts, SummaryLog, N, Sym, os.str()));
+ auto R = llvm::make_unique<CFRefReport>(*overAutorelease, LOpts, SummaryLog,
+ N, Sym, os.str());
+ Ctx.emitReport(std::move(R));
}
return nullptr;
void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,
CheckerContext &Ctx) const {
- ProgramStateRef state = Ctx.getState();
+ ExplodedNode *Pred = processReturn(RS, Ctx);
+
+ // Created state cached out.
+ if (!Pred) {
+ return;
+ }
+
+ ProgramStateRef state = Pred->getState();
RefBindingsTy B = state->get<RefBindings>();
- ExplodedNode *Pred = Ctx.getPredecessor();
// Don't process anything within synthesized bodies.
const LocationContext *LCtx = Pred->getLocationContext();
check::PostStmt<ObjCBoxedExpr>,
check::PostStmt<ObjCIvarRefExpr>,
check::PostCall,
- check::PreStmt<ReturnStmt>,
check::RegionChanges,
eval::Assume,
eval::Call > {
const LocationContext* LCtx,
const CallEvent *Call) const;
- void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
- void checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C,
+ ExplodedNode* checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C,
ExplodedNode *Pred, RetEffect RE, RefVal X,
SymbolRef Sym, ProgramStateRef state) const;
ProgramStateRef
handleAutoreleaseCounts(ProgramStateRef state, ExplodedNode *Pred,
const ProgramPointTag *Tag, CheckerContext &Ctx,
- SymbolRef Sym, RefVal V) const;
+ SymbolRef Sym,
+ RefVal V,
+ const ReturnStmt *S=nullptr) const;
ExplodedNode *processLeaks(ProgramStateRef state,
SmallVectorImpl<SymbolRef> &Leaked,
CheckerContext &Ctx,
ExplodedNode *Pred = nullptr) const;
+
+private:
+ /// Perform the necessary checks and state adjustments at the end of the
+ /// function.
+ /// \p S Return statement, may be null.
+ ExplodedNode * processReturn(const ReturnStmt *S, CheckerContext &C) const;
};
//===----------------------------------------------------------------------===//
</dict>
</array>
</dict>
- <dict>
- <key>kind</key><string>event</string>
- <key>location</key>
- <dict>
- <key>line</key><integer>110</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <key>ranges</key>
- <array>
- <array>
- <dict>
- <key>line</key><integer>110</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>110</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- <array>
- <dict>
- <key>line</key><integer>110</integer>
- <key>col</key><integer>10</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>110</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- </array>
- <key>depth</key><integer>0</integer>
- <key>extended_message</key>
- <string>Object returned to caller with a +0 retain count</string>
- <key>message</key>
- <string>Object returned to caller with a +0 retain count</string>
- </dict>
<dict>
<key>kind</key><string>event</string>
<key>location</key>
</dict>
</array>
</dict>
- <dict>
- <key>kind</key><string>event</string>
- <key>location</key>
- <dict>
- <key>line</key><integer>115</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <key>ranges</key>
- <array>
- <array>
- <dict>
- <key>line</key><integer>115</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>115</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- <array>
- <dict>
- <key>line</key><integer>115</integer>
- <key>col</key><integer>10</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>115</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- </array>
- <key>depth</key><integer>0</integer>
- <key>extended_message</key>
- <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
- <key>message</key>
- <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
- </dict>
<dict>
<key>kind</key><string>event</string>
<key>location</key>
</dict>
</array>
</dict>
- <dict>
- <key>kind</key><string>event</string>
- <key>location</key>
- <dict>
- <key>line</key><integer>121</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <key>ranges</key>
- <array>
- <array>
- <dict>
- <key>line</key><integer>121</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>121</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- <array>
- <dict>
- <key>line</key><integer>121</integer>
- <key>col</key><integer>10</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>121</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- </array>
- <key>depth</key><integer>0</integer>
- <key>extended_message</key>
- <string>Object returned to caller with a +0 retain count</string>
- <key>message</key>
- <string>Object returned to caller with a +0 retain count</string>
- </dict>
<dict>
<key>kind</key><string>event</string>
<key>location</key>
</dict>
</array>
</dict>
- <dict>
- <key>kind</key><string>event</string>
- <key>location</key>
- <dict>
- <key>line</key><integer>126</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <key>ranges</key>
- <array>
- <array>
- <dict>
- <key>line</key><integer>126</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>126</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- <array>
- <dict>
- <key>line</key><integer>126</integer>
- <key>col</key><integer>10</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>126</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- </array>
- <key>depth</key><integer>0</integer>
- <key>extended_message</key>
- <string>Object returned to caller with a +0 retain count</string>
- <key>message</key>
- <string>Object returned to caller with a +0 retain count</string>
- </dict>
<dict>
<key>kind</key><string>event</string>
<key>location</key>
</dict>
</array>
</dict>
- <dict>
- <key>kind</key><string>event</string>
- <key>location</key>
- <dict>
- <key>line</key><integer>131</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <key>ranges</key>
- <array>
- <array>
- <dict>
- <key>line</key><integer>131</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>131</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- <array>
- <dict>
- <key>line</key><integer>131</integer>
- <key>col</key><integer>10</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>131</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- </array>
- <key>depth</key><integer>0</integer>
- <key>extended_message</key>
- <string>Object returned to caller with a +0 retain count</string>
- <key>message</key>
- <string>Object returned to caller with a +0 retain count</string>
- </dict>
<dict>
<key>kind</key><string>event</string>
<key>location</key>
</dict>
</array>
</dict>
- <dict>
- <key>kind</key><string>event</string>
- <key>location</key>
- <dict>
- <key>line</key><integer>136</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <key>ranges</key>
- <array>
- <array>
- <dict>
- <key>line</key><integer>136</integer>
- <key>col</key><integer>3</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>136</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- <array>
- <dict>
- <key>line</key><integer>136</integer>
- <key>col</key><integer>10</integer>
- <key>file</key><integer>0</integer>
- </dict>
- <dict>
- <key>line</key><integer>136</integer>
- <key>col</key><integer>15</integer>
- <key>file</key><integer>0</integer>
- </dict>
- </array>
- </array>
- <key>depth</key><integer>0</integer>
- <key>extended_message</key>
- <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
- <key>message</key>
- <string>Object returned to caller as an owning reference (single retain count transferred to caller)</string>
- </dict>
<dict>
<key>kind</key><string>event</string>
<key>location</key>
</array>
<key>files</key>
<array>
- <string>/clang/test/Analysis/retain-release-path-notes.m</string>
+ <string>/test/Analysis/retain-release-path-notes.m</string>
</array>
</dict>
</plist>
return (__bridge NSDictionary *)testDict;
#if HAS_ARC
// expected-warning@-2 {{Potential leak of an object stored into 'testDict'}}
- // expected-note@-3 {{Object returned to caller as an owning reference (single retain count transferred to caller)}}
- // expected-note@-4 {{Object leaked: object allocated and stored into 'testDict' is returned from a method managed by Automatic Reference Counting}}
+ // expected-note@-3 {{Object leaked: object allocated and stored into 'testDict' is returned from a method managed by Automatic Reference Counting}}
#endif
}
CFTypeRef CFCopyRuleViolation () {
CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object of type CFTypeRef with a +0 retain count}}
- return object; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
+ return object; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
}
CFTypeRef CFGetRuleViolation () {
CFTypeRef object = CFCreateSomething(); // expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object of type CFTypeRef with a +1 retain count}}
- return object; // expected-warning{{leak}} expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'object' is returned from a function whose name ('CFGetRuleViolation') does not contain 'Copy' or 'Create'. This violates the naming convention rules given in the Memory Management Guide for Core Foundation}}
+ return object; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'object' is returned from a function whose name ('CFGetRuleViolation') does not contain 'Copy' or 'Create'. This violates the naming convention rules given in the Memory Management Guide for Core Foundation}}
}
@implementation Foo (FundamentalMemoryManagementRules)
- (id)copyViolation {
id result = self.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}}
- return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
+ return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
}
- (id)copyViolationIndexedSubscript {
id result = self[0]; // expected-note{{Subscript returns an Objective-C object with a +0 retain count}}
- return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
+ return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
}
- (id)copyViolationKeyedSubscript {
id result = self[self]; // expected-note{{Subscript returns an Objective-C object with a +0 retain count}}
- return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
+ return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
}
- (id)getViolation {
id result = [[Foo alloc] init]; // expected-note{{Method returns an instance of Foo with a +1 retain count}}
- return result; // expected-warning{{leak}} expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'result' is returned from a method whose name ('getViolation') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'. This violates the naming convention rules given in the Memory Management Guide for Cocoa}}
+ return result; // expected-warning{{leak}} expected-note{{Object leaked: object allocated and stored into 'result' is returned from a method whose name ('getViolation') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'. This violates the naming convention rules given in the Memory Management Guide for Cocoa}}
}
- (id)copyAutorelease {