diff options
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h | 138 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp | 106 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp | 3 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp | 36 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/Calls.cpp | 69 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 33 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 1 | ||||
-rw-r--r-- | test/Analysis/objc-subscript.m | 89 | ||||
-rw-r--r-- | test/Analysis/retain-release-path-notes.m | 17 |
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}} |