]> granicus.if.org Git - clang/commitdiff
[analyzer] Combine all ObjC message CallEvents into ObjCMethodCall.
authorJordan Rose <jordan_rose@apple.com>
Wed, 18 Jul 2012 21:59:51 +0000 (21:59 +0000)
committerJordan Rose <jordan_rose@apple.com>
Wed, 18 Jul 2012 21:59:51 +0000 (21:59 +0000)
As pointed out by Anna, we only differentiate between explicit message sends

This also adds support for ObjCSubscriptExprs, which are basically the same
as properties in many ways. We were already checking these, but not emitting
nice messages for them.

This depends on the llvm::PointerIntPair change in r160456.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160461 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
lib/StaticAnalyzer/Core/Calls.cpp
lib/StaticAnalyzer/Core/ExprEngine.cpp
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
test/Analysis/objc-subscript.m [new file with mode: 0644]
test/Analysis/retain-release-path-notes.m

index 7944d7aa6e76b2013e18b7557779fcfd433ee9ea..e0ed3cdde0797348d488f46a55b7174b946962f3 100644 (file)
@@ -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 {
index 69b331c16cc94d987b36b29fd794eff29f999a61..17ed692a6c189341585d7d32a9928a575373f78c 100644 (file)
@@ -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));
index 842cbc5f59567da3897860f0e0844bdc651ade74..0e9efaa5ade806ca051b2ea1d1ad3f7c1bffe45e 100644 (file)
@@ -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 {}
index c44fc67d265bf143669ea566da6d072eddca2bcc..aca5f798d465f26724c7f37f10afe116f045f992 100644 (file)
@@ -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;
           }
         }
index ba56364d513136a6b60851f5a4a12dc6e8525e72..5c161ba27b90d806208670a65d895d0818bb0867 100644 (file)
@@ -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());
 }
 
index fb99d916040772ed54d92ffbe7139078753703c1..895e20eca175100fd991619f1200e125e438f1dc 100644 (file)
@@ -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;
     }
index 493ca91b48c1e86f89e07df39cdaf2524ac4dfc9..7cedb7ac5e85813520dcc6dbc12218c26335378c 100644 (file)
@@ -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 (file)
index 0000000..324bf1c
--- /dev/null
@@ -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
index be6336b59f77a122f1160d5baa6b8b52ee684723..5b702214a4826a79fb4c4f91a2433a648c776626 100644 (file)
@@ -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}}