aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h138
-rw-r--r--lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp106
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp36
-rw-r--r--lib/StaticAnalyzer/Core/Calls.cpp69
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp33
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp1
-rw-r--r--test/Analysis/objc-subscript.m89
-rw-r--r--test/Analysis/retain-release-path-notes.m17
9 files changed, 302 insertions, 190 deletions
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
index 7944d7aa6e..e0ed3cdde0 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
@@ -44,10 +44,7 @@ enum CallEventKind {
CE_CXXAllocator,
CE_BEG_FUNCTION_CALLS = CE_Function,
CE_END_FUNCTION_CALLS = CE_CXXAllocator,
- CE_ObjCMessage,
- CE_ObjCPropertyAccess,
- CE_BEG_OBJC_CALLS = CE_ObjCMessage,
- CE_END_OBJC_CALLS = CE_ObjCPropertyAccess
+ CE_ObjCMessage
};
@@ -601,41 +598,42 @@ public:
}
};
+/// \brief Represents the ways an Objective-C message send can occur.
+//
+// Note to maintainers: OCM_Message should always be last, since it does not
+// need to fit in the Data field's low bits.
+enum ObjCMessageKind {
+ OCM_PropertyAccess,
+ OCM_Subscript,
+ OCM_Message
+};
+
/// \brief Represents any expression that calls an Objective-C method.
+///
+/// This includes all of the kinds listed in ObjCMessageKind.
class ObjCMethodCall : public CallEvent {
friend class CallEvent;
-protected:
- ObjCMethodCall(const ObjCMessageExpr *Msg, ProgramStateRef St,
- const LocationContext *LCtx, Kind K)
- : CallEvent(Msg, St, LCtx, K) {}
+ const PseudoObjectExpr *getContainingPseudoObjectExpr() const;
+protected:
void getExtraInvalidatedRegions(RegionList &Regions) const;
QualType getDeclaredResultType() const;
public:
- const ObjCMessageExpr *getOriginExpr() const {
- return cast<ObjCMessageExpr>(CallEvent::getOriginExpr());
+ ObjCMethodCall(const ObjCMessageExpr *Msg, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : CallEvent(Msg, St, LCtx, CE_ObjCMessage) {
+ Data = 0;
}
- Selector getSelector() const {
- return getOriginExpr()->getSelector();
- }
- bool isInstanceMessage() const {
- return getOriginExpr()->isInstanceMessage();
- }
- ObjCMethodFamily getMethodFamily() const {
- return getOriginExpr()->getMethodFamily();
+ const ObjCMessageExpr *getOriginExpr() const {
+ return cast<ObjCMessageExpr>(CallEvent::getOriginExpr());
}
-
const ObjCMethodDecl *getDecl() const {
return getOriginExpr()->getMethodDecl();
}
-
- SourceRange getSourceRange() const {
- return getOriginExpr()->getSourceRange();
- }
unsigned getNumArgs() const {
return getOriginExpr()->getNumArgs();
}
@@ -643,18 +641,21 @@ public:
return getOriginExpr()->getArg(Index);
}
+ bool isInstanceMessage() const {
+ return getOriginExpr()->isInstanceMessage();
+ }
+ ObjCMethodFamily getMethodFamily() const {
+ return getOriginExpr()->getMethodFamily();
+ }
+ Selector getSelector() const {
+ return getOriginExpr()->getSelector();
+ }
+
+ SourceRange getSourceRange() const;
+
/// \brief Returns the value of the receiver at the time of this call.
SVal getReceiverSVal() const;
- /// \brief Returns the expression for the receiver of this message if it is
- /// an instance message.
- ///
- /// Returns NULL otherwise.
- /// \sa ObjCMessageExpr::getInstanceReceiver()
- const Expr *getInstanceReceiverExpr() const {
- return getOriginExpr()->getInstanceReceiver();
- }
-
/// \brief Get the interface for the receiver.
///
/// This works whether this is an instance message or a class message.
@@ -663,14 +664,27 @@ public:
return getOriginExpr()->getReceiverInterface();
}
- SourceRange getReceiverSourceRange() const {
- return getOriginExpr()->getReceiverRange();
+ ObjCMessageKind getMessageKind() const;
+
+ bool isSetter() const {
+ switch (getMessageKind()) {
+ case OCM_Message:
+ llvm_unreachable("This is not a pseudo-object access!");
+ case OCM_PropertyAccess:
+ return getNumArgs() > 0;
+ case OCM_Subscript:
+ return getNumArgs() > 1;
+ }
+ llvm_unreachable("Unknown message kind");
}
const Decl *getDefinition(bool &IsDynamicDispatch) const {
IsDynamicDispatch = true;
const ObjCMethodDecl *MD = getDecl();
+ if (!MD)
+ return 0;
+
for (Decl::redecl_iterator I = MD->redecls_begin(), E = MD->redecls_end();
I != E; ++I) {
if (cast<ObjCMethodDecl>(*I)->isThisDeclarationADefinition())
@@ -690,62 +704,14 @@ public:
return getArgExpr(Index)->getSourceRange();
}
-
param_iterator param_begin(bool UseDefinitionParams = false) const;
param_iterator param_end(bool UseDefinitionParams = false) const;
static bool classof(const CallEvent *CA) {
- return CA->getKind() >= CE_BEG_OBJC_CALLS &&
- CA->getKind() <= CE_END_OBJC_CALLS;
- }
-};
-
-/// \brief Represents an explicit message send to an Objective-C object.
-///
-/// Example: [obj descriptionWithLocale:locale];
-class ObjCMessageSend : public ObjCMethodCall {
-public:
- ObjCMessageSend(const ObjCMessageExpr *Msg, ProgramStateRef St,
- const LocationContext *LCtx)
- : ObjCMethodCall(Msg, St, LCtx, CE_ObjCMessage) {}
-
- static bool classof(const CallEvent *CA) {
return CA->getKind() == CE_ObjCMessage;
}
};
-/// \brief Represents an Objective-C property getter or setter invocation.
-///
-/// Example: obj.prop += 1;
-class ObjCPropertyAccess : public ObjCMethodCall {
-public:
- ObjCPropertyAccess(const ObjCPropertyRefExpr *PE, SourceRange range,
- const ObjCMessageExpr *Msg, const ProgramStateRef St,
- const LocationContext *LCtx)
- : ObjCMethodCall(Msg, St, LCtx, CE_ObjCPropertyAccess) {
- Data = PE;
- }
-
- /// \brief Returns true if this property access is calling the setter method.
- bool isSetter() const {
- return getNumArgs() > 0;
- }
-
- SourceRange getSourceRange() const;
-
- /// \brief Return the property reference part of this access.
- ///
- /// In the expression "obj.prop += 1", the property reference expression is
- /// "obj.prop".
- const ObjCPropertyRefExpr *getPropertyExpr() const {
- return static_cast<const ObjCPropertyRefExpr *>(Data);
- }
-
- static bool classof(const CallEvent *CA) {
- return CA->getKind() == CE_ObjCPropertyAccess;
- }
-};
-
// FIXME: Use a .def or .td file for this.
#define DISPATCH(fn) \
@@ -765,9 +731,7 @@ public:
case CE_CXXAllocator: \
return cast<CXXAllocatorCall>(this)->fn(); \
case CE_ObjCMessage: \
- return cast<ObjCMessageSend>(this)->fn(); \
- case CE_ObjCPropertyAccess: \
- return cast<ObjCPropertyAccess>(this)->fn(); \
+ return cast<ObjCMethodCall>(this)->fn(); \
}
#define DISPATCH_ARG(fn, arg) \
@@ -787,9 +751,7 @@ public:
case CE_CXXAllocator: \
return cast<CXXAllocatorCall>(this)->fn(arg); \
case CE_ObjCMessage: \
- return cast<ObjCMessageSend>(this)->fn(arg); \
- case CE_ObjCPropertyAccess: \
- return cast<ObjCPropertyAccess>(this)->fn(arg); \
+ return cast<ObjCMethodCall>(this)->fn(arg); \
}
inline void CallEvent::getExtraInvalidatedRegions(RegionList &Regions) const {
diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index 69b331c16c..17ed692a6c 100644
--- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -34,6 +34,7 @@ class CallAndMessageChecker
mutable OwningPtr<BugType> BT_call_arg;
mutable OwningPtr<BugType> BT_msg_undef;
mutable OwningPtr<BugType> BT_objc_prop_undef;
+ mutable OwningPtr<BugType> BT_objc_subscript_undef;
mutable OwningPtr<BugType> BT_msg_arg;
mutable OwningPtr<BugType> BT_msg_ret;
public:
@@ -45,8 +46,8 @@ public:
private:
static bool PreVisitProcessArg(CheckerContext &C, SVal V,
SourceRange argRange, const Expr *argEx,
- const bool checkUninitFields,
- const char *BT_desc, OwningPtr<BugType> &BT);
+ bool IsFirstArgument, bool checkUninitFields,
+ const CallEvent &Call, OwningPtr<BugType> &BT);
static void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE);
void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
@@ -75,18 +76,46 @@ void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C,
C.EmitReport(R);
}
+StringRef describeUninitializedArgumentInCall(const CallEvent &Call,
+ bool IsFirstArgument) {
+ switch (Call.getKind()) {
+ case CE_ObjCMessage: {
+ const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
+ switch (Msg.getMessageKind()) {
+ case OCM_Message:
+ return "Argument in message expression is an uninitialized value";
+ case OCM_PropertyAccess:
+ assert(Msg.isSetter() && "Getters have no args");
+ return "Argument for property setter is an uninitialized value";
+ case OCM_Subscript:
+ if (Msg.isSetter() && IsFirstArgument)
+ return "Argument for subscript setter is an uninitialized value";
+ return "Subscript index is an uninitialized value";
+ }
+ llvm_unreachable("Unknown message kind.");
+ }
+ case CE_Block:
+ return "Block call argument is an uninitialized value";
+ default:
+ return "Function call argument is an uninitialized value";
+ }
+}
+
bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
SVal V, SourceRange argRange,
const Expr *argEx,
- const bool checkUninitFields,
- const char *BT_desc,
+ bool IsFirstArgument,
+ bool checkUninitFields,
+ const CallEvent &Call,
OwningPtr<BugType> &BT) {
if (V.isUndef()) {
if (ExplodedNode *N = C.generateSink()) {
LazyInit_BT("Uninitialized argument value", BT);
// Generate a report for this bug.
- BugReport *R = new BugReport(*BT, BT_desc, N);
+ StringRef Desc = describeUninitializedArgumentInCall(Call,
+ IsFirstArgument);
+ BugReport *R = new BugReport(*BT, Desc, N);
R->addRange(argRange);
if (argEx)
R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, argEx,
@@ -148,7 +177,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
if (F.Find(D->getRegion())) {
if (ExplodedNode *N = C.generateSink()) {
- LazyInit_BT(BT_desc, BT);
+ LazyInit_BT("Uninitialized argument value", BT);
SmallString<512> Str;
llvm::raw_svector_ostream os(Str);
os << "Passed-by-value struct argument contains uninitialized data";
@@ -220,32 +249,15 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
(D && D->getBody()));
OwningPtr<BugType> *BT;
- const char *Desc;
-
- switch (Call.getKind()) {
- case CE_ObjCPropertyAccess:
- BT = &BT_msg_arg;
- // Getters do not have arguments, so we don't need to worry about this.
- Desc = "Argument for property setter is an uninitialized value";
- break;
- case CE_ObjCMessage:
+ if (isa<ObjCMethodCall>(Call))
BT = &BT_msg_arg;
- Desc = "Argument in message expression is an uninitialized value";
- break;
- case CE_Block:
+ else
BT = &BT_call_arg;
- Desc = "Block call argument is an uninitialized value";
- break;
- default:
- BT = &BT_call_arg;
- Desc = "Function call argument is an uninitialized value";
- break;
- }
for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i)
- if (PreVisitProcessArg(C, Call.getArgSVal(i),
- Call.getArgSourceRange(i), Call.getArgExpr(i),
- checkUninitFields, Desc, *BT))
+ if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
+ Call.getArgExpr(i), /*IsFirstArgument=*/i == 0,
+ checkUninitFields, Call, *BT))
return;
}
@@ -255,22 +267,36 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
if (recVal.isUndef()) {
if (ExplodedNode *N = C.generateSink()) {
BugType *BT = 0;
- if (isa<ObjCPropertyAccess>(msg)) {
- if (!BT_objc_prop_undef)
- BT_objc_prop_undef.reset(new BuiltinBug("Property access on an "
- "uninitialized object pointer"));
- BT = BT_objc_prop_undef.get();
- } else {
+ switch (msg.getMessageKind()) {
+ case OCM_Message:
if (!BT_msg_undef)
BT_msg_undef.reset(new BuiltinBug("Receiver in message expression "
"is an uninitialized value"));
BT = BT_msg_undef.get();
+ break;
+ case OCM_PropertyAccess:
+ if (!BT_objc_prop_undef)
+ BT_objc_prop_undef.reset(new BuiltinBug("Property access on an "
+ "uninitialized object "
+ "pointer"));
+ BT = BT_objc_prop_undef.get();
+ break;
+ case OCM_Subscript:
+ if (!BT_objc_subscript_undef)
+ BT_objc_subscript_undef.reset(new BuiltinBug("Subscript access on an "
+ "uninitialized object "
+ "pointer"));
+ BT = BT_objc_subscript_undef.get();
+ break;
}
+ assert(BT && "Unknown message kind.");
+
BugReport *R = new BugReport(*BT, BT->getName(), N);
- R->addRange(msg.getReceiverSourceRange());
+ const ObjCMessageExpr *ME = msg.getOriginExpr();
+ R->addRange(ME->getReceiverRange());
// FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
- if (const Expr *ReceiverE = msg.getInstanceReceiverExpr())
+ if (const Expr *ReceiverE = ME->getInstanceReceiver())
R->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
ReceiverE,
R));
@@ -302,17 +328,19 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
new BuiltinBug("Receiver in message expression is "
"'nil' and returns a garbage value"));
+ const ObjCMessageExpr *ME = msg.getOriginExpr();
+
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
- os << "The receiver of message '" << msg.getSelector().getAsString()
+ os << "The receiver of message '" << ME->getSelector().getAsString()
<< "' is nil and returns a value of type '";
msg.getResultType().print(os, C.getLangOpts());
os << "' that will be garbage";
BugReport *report = new BugReport(*BT_msg_ret, os.str(), N);
- report->addRange(msg.getReceiverSourceRange());
+ report->addRange(ME->getReceiverRange());
// FIXME: This won't track "self" in messages to super.
- if (const Expr *receiver = msg.getInstanceReceiverExpr()) {
+ if (const Expr *receiver = ME->getInstanceReceiver()) {
report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
receiver,
report));
diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
index 842cbc5f59..0e9efaa5ad 100644
--- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
@@ -81,8 +81,7 @@ public:
///
/// This will be called before the analyzer core processes the method call.
/// This is called for any action which produces an Objective-C message send,
- /// including explicit message syntax and property access. See the subclasses
- /// of ObjCMethodCall for more details.
+ /// including explicit message syntax and property access.
///
/// check::PreObjCMessage
void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {}
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index c44fc67d26..aca5f798d4 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -952,8 +952,7 @@ RetainSummaryManager::getSummary(const CallEvent &Call,
case CE_CXXAllocator:
// FIXME: These calls are currently unsupported.
return getPersistentStopSummary();
- case CE_ObjCMessage:
- case CE_ObjCPropertyAccess: {
+ case CE_ObjCMessage: {
const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
if (Msg.isInstanceMessage())
Summ = getInstanceMethodSummary(Msg, State);
@@ -1911,20 +1910,6 @@ static bool isNumericLiteralExpression(const Expr *E) {
isa<CXXBoolLiteralExpr>(E);
}
-static bool isPropertyAccess(const Stmt *S, ParentMap &PM) {
- unsigned maxDepth = 4;
- while (S && maxDepth) {
- if (const PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(S)) {
- if (!isa<ObjCMessageExpr>(PO->getSyntacticForm()))
- return true;
- return false;
- }
- S = PM.getParent(S);
- --maxDepth;
- }
- return false;
-}
-
PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
@@ -1989,10 +1974,19 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
os << "function call";
}
else {
- assert(isa<ObjCMessageExpr>(S));
- // The message expression may have between written directly or as
- // a property access. Lazily determine which case we are looking at.
- os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method");
+ assert(isa<ObjCMessageExpr>(S));
+ ObjCMethodCall Call(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
+ switch (Call.getMessageKind()) {
+ case OCM_Message:
+ os << "Method";
+ break;
+ case OCM_PropertyAccess:
+ os << "Property";
+ break;
+ case OCM_Subscript:
+ os << "Subscript";
+ break;
+ }
}
if (CurrV.getObjKind() == RetEffect::CF) {
@@ -2824,7 +2818,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(),
hasErr, C);
if (hasErr) {
- ErrorRange = MsgInvocation->getReceiverSourceRange();
+ ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange();
ErrorSym = Sym;
}
}
diff --git a/lib/StaticAnalyzer/Core/Calls.cpp b/lib/StaticAnalyzer/Core/Calls.cpp
index ba56364d51..5c161ba27b 100644
--- a/lib/StaticAnalyzer/Core/Calls.cpp
+++ b/lib/StaticAnalyzer/Core/Calls.cpp
@@ -518,7 +518,7 @@ SVal ObjCMethodCall::getReceiverSVal() const {
if (!isInstanceMessage())
return UnknownVal();
- if (const Expr *Base = getInstanceReceiverExpr())
+ if (const Expr *Base = getOriginExpr()->getInstanceReceiver())
return getSVal(Base);
// An instance message with no expression means we are sending to super.
@@ -529,10 +529,67 @@ SVal ObjCMethodCall::getReceiverSVal() const {
return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx));
}
-SourceRange ObjCPropertyAccess::getSourceRange() const {
- const ParentMap &PM = getLocationContext()->getParentMap();
- const ObjCMessageExpr *ME = getOriginExpr();
- const PseudoObjectExpr *PO = cast<PseudoObjectExpr>(PM.getParent(ME));
- return PO->getSourceRange();
+SourceRange ObjCMethodCall::getSourceRange() const {
+ switch (getMessageKind()) {
+ case OCM_Message:
+ return getOriginExpr()->getSourceRange();
+ case OCM_PropertyAccess:
+ case OCM_Subscript:
+ return getContainingPseudoObjectExpr()->getSourceRange();
+ }
+}
+
+typedef llvm::PointerIntPair<const PseudoObjectExpr *, 2> ObjCMessageDataTy;
+
+const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const {
+ assert(Data != 0 && "Lazy lookup not yet performed.");
+ assert(getMessageKind() != OCM_Message && "Explicit message send.");
+ return ObjCMessageDataTy::getFromOpaqueValue(Data).getPointer();
+}
+
+ObjCMessageKind ObjCMethodCall::getMessageKind() const {
+ if (Data == 0) {
+ ParentMap &PM = getLocationContext()->getParentMap();
+ const Stmt *S = PM.getParent(getOriginExpr());
+ if (const PseudoObjectExpr *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) {
+ const Expr *Syntactic = POE->getSyntacticForm();
+
+ // This handles the funny case of assigning to the result of a getter.
+ // This can happen if the getter returns a non-const reference.
+ if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Syntactic))
+ Syntactic = BO->getLHS();
+
+ ObjCMessageKind K;
+ switch (Syntactic->getStmtClass()) {
+ case Stmt::ObjCPropertyRefExprClass:
+ K = OCM_PropertyAccess;
+ break;
+ case Stmt::ObjCSubscriptRefExprClass:
+ K = OCM_Subscript;
+ break;
+ default:
+ // FIXME: Can this ever happen?
+ K = OCM_Message;
+ break;
+ }
+
+ if (K != OCM_Message) {
+ const_cast<ObjCMethodCall *>(this)->Data
+ = ObjCMessageDataTy(POE, K).getOpaqueValue();
+ assert(getMessageKind() == K);
+ return K;
+ }
+ }
+
+ const_cast<ObjCMethodCall *>(this)->Data
+ = ObjCMessageDataTy(0, 1).getOpaqueValue();
+ assert(getMessageKind() == OCM_Message);
+ return OCM_Message;
+ }
+
+ ObjCMessageDataTy Info = ObjCMessageDataTy::getFromOpaqueValue(Data);
+ if (!Info.getPointer())
+ return OCM_Message;
+ return static_cast<ObjCMessageKind>(Info.getInt());
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index fb99d91604..895e20eca1 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -890,35 +890,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::ObjCMessageExprClass: {
Bldr.takeNodes(Pred);
- // Is this a property access?
-
- const LocationContext *LCtx = Pred->getLocationContext();
- const ParentMap &PM = LCtx->getParentMap();
- const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(S);
- bool evaluated = false;
-
- if (const PseudoObjectExpr *PO =
- dyn_cast_or_null<PseudoObjectExpr>(PM.getParent(S))) {
- const Expr *syntactic = PO->getSyntacticForm();
-
- // This handles the funny case of assigning to the result of a getter.
- // This can happen if the getter returns a non-const reference.
- if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(syntactic))
- syntactic = BO->getLHS();
-
- if (const ObjCPropertyRefExpr *PR =
- dyn_cast<ObjCPropertyRefExpr>(syntactic)) {
- VisitObjCMessage(ObjCPropertyAccess(PR, PO->getSourceRange(), ME,
- Pred->getState(), LCtx),
- Pred, Dst);
- evaluated = true;
- }
- }
-
- if (!evaluated)
- VisitObjCMessage(ObjCMessageSend(ME, Pred->getState(), LCtx),
- Pred, Dst);
-
+ VisitObjCMessage(ObjCMethodCall(cast<ObjCMessageExpr>(S),
+ Pred->getState(),
+ Pred->getLocationContext()),
+ Pred, Dst);
Bldr.addNodes(Dst);
break;
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 493ca91b48..7cedb7ac5e 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -306,7 +306,6 @@ bool ExprEngine::inlineCall(ExplodedNodeSet &Dst,
break;
}
case CE_ObjCMessage:
- case CE_ObjCPropertyAccess:
// These always use dynamic dispatch; enabling inlining means assuming
// that a particular method will be called at runtime.
llvm_unreachable("Dynamic dispatch should be handled above.");
diff --git a/test/Analysis/objc-subscript.m b/test/Analysis/objc-subscript.m
new file mode 100644
index 0000000000..324bf1c785
--- /dev/null
+++ b/test/Analysis/objc-subscript.m
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount -analyzer-store=region -verify -Wno-objc-root-class %s
+
+typedef signed char BOOL;
+typedef unsigned int NSUInteger;
+
+@interface NSObject
++(id)alloc;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+-(id)retain;
+@end
+
+@interface Subscriptable : NSObject
+- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index;
+- (id)objectAtIndexedSubscript:(NSUInteger)index;
+
+- (void)setObject:(id)obj forKeyedSubscript:(id)key;
+- (id)objectForKeyedSubscript:(id)key;
+@end
+
+@interface Test : Subscriptable
+@end
+
+@implementation Test
+
+// <rdar://problem/6946338> for subscripting
+- (id)storeDoesNotRetain {
+ Test *cell = [[[Test alloc] init] autorelease];
+
+ NSObject *string1 = [[NSObject alloc] init]; // expected-warning {{Potential leak}}
+ cell[0] = string1;
+ cell[self] = string1;
+ cell[string1] = self;
+
+ return cell;
+}
+
+// <rdar://problem/8824416> for subscripting
+- (id)getDoesNotRetain:(BOOL)keyed {
+ if (keyed)
+ return [self[self] autorelease]; // expected-warning{{Object sent -autorelease too many times}}
+ else
+ return [self[0] autorelease]; // expected-warning{{Object sent -autorelease too many times}}
+}
+
+// <rdar://problem/9241180> for subscripting
+- (id)testUninitializedObject:(BOOL)keyed {
+ Test *o;
+ if (keyed) {
+ if (o[self]) // expected-warning {{Subscript access on an uninitialized object pointer}}
+ return o; // no-warning (sink)
+ } else {
+ if (o[0]) // expected-warning {{Subscript access on an uninitialized object pointer}}
+ return o; // no-warning (sink)
+ }
+ return self;
+}
+
+- (void)testUninitializedArgument:(id)input testCase:(unsigned)testCase {
+ NSUInteger i;
+ id o;
+
+ switch (testCase) {
+ case 0:
+ self[0] = o; // expected-warning {{Argument for subscript setter is an uninitialized value}}
+ break;
+ case 1:
+ self[i] = input; // expected-warning {{Subscript index is an uninitialized value}}
+ break;
+ case 2:
+ (void)self[i]; // expected-warning {{Subscript index is an uninitialized value}}
+ break;
+ case 3:
+ self[input] = o; // expected-warning {{Argument for subscript setter is an uninitialized value}}
+ break;
+ case 4:
+ self[o] = input; // expected-warning {{Subscript index is an uninitialized value}}
+ break;
+ case 5:
+ (void)self[o]; // expected-warning {{Subscript index is an uninitialized value}}
+ break;
+ default:
+ break;
+ }
+
+}
+
+@end
diff --git a/test/Analysis/retain-release-path-notes.m b/test/Analysis/retain-release-path-notes.m
index be6336b59f..5b702214a4 100644
--- a/test/Analysis/retain-release-path-notes.m
+++ b/test/Analysis/retain-release-path-notes.m
@@ -1,9 +1,5 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -analyzer-output=text -verify %s
-// This actually still works after the pseudo-object refactor, it just
-// uses messages that say 'method' instead of 'property'. Ted wanted
-// this xfailed and filed as a bug. rdar://problem/10402993
-
/***
This file is for testing the path-sensitive notes for retain/release errors.
Its goal is to have simple branch coverage of any path-based diagnostics,
@@ -28,6 +24,9 @@ GC-specific notes should go in retain-release-path-notes-gc.m.
@interface Foo : NSObject
- (id)methodWithValue;
@property(retain) id propertyValue;
+
+- (id)objectAtIndexedSubscript:(unsigned)index;
+- (id)objectForKeyedSubscript:(id)key;
@end
typedef struct CFType *CFTypeRef;
@@ -119,6 +118,16 @@ CFTypeRef CFGetRuleViolation () {
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}}
}
+- (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}}
+}
+
+- (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}}
+}
+
- (id)getViolation {
id result = [[Foo alloc] init]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +1 retain count}}
return result; // 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}}